From 0a11e8ba1d328a0d05d7ca0953a3c403d916b966 Mon Sep 17 00:00:00 2001 From: Allen Cai Date: Tue, 11 Aug 2026 17:25:00 +0800 Subject: [PATCH 1/9] Update project code and documentation - Create CODEOWNERS file for code review ownership - Add bug report and feature request issue templates - Introduce Codex PR review rubric for pull requests - Configure Dependabot for NuGet and GitHub Actions updates - Set up CI workflow for building and testing on push and pull requests - Implement optional Codex PR review workflow with OpenAI integration - Update .gitignore to exclude additional build and user-specific files - Add repository guidance for AI coding agents in AGENTS.md - Establish Contributor Covenant Code of Conduct - Create CONTRIBUTING.md with development setup and guidelines - Update LICENSE to reflect new copyright holder - Revise README.md for clarity and additional usage instructions - Add SECURITY.md for reporting vulnerabilities - Include sample output and project layout in README - Add nuget.config for package source configuration - Remove unused CarModel class and replace with RepoModel - Update project file to target .NET 10 and include necessary packages - Refactor Program.cs to improve structure and functionality - Modify publish profiles for .NET 10 targeting --- .editorconfig | 280 ++++++++++++++++++ .github/CODEOWNERS | 2 + .github/ISSUE_TEMPLATE/bug_report.yml | 48 +++ .github/ISSUE_TEMPLATE/feature_request.yml | 23 ++ .github/codex/prompts/review.md | 12 + .github/dependabot.yml | 13 + .github/workflows/ci.yml | 55 ++++ .github/workflows/codex-review.yml | 81 +++++ .gitignore | 224 ++++++++++++-- AGENTS.md | 22 ++ CODE_OF_CONDUCT.md | 66 +++++ CONTRIBUTING.md | 48 +++ LICENSE | 4 +- README.md | 106 +++++-- SECURITY.md | 29 ++ SampleSnapshoot.png | Bin 47885 -> 52533 bytes nuget.config | 7 + src/CrawlerSamples.ConsoleApp/CarModel.cs | 19 -- .../CrawlerSamples.ConsoleApp.csproj | 24 +- src/CrawlerSamples.ConsoleApp/Program.cs | 175 ++++++----- .../PublishWin64ReleaseToLocalFolder.pubxml | 11 +- .../PublishWin86ReleaseToLocalFolder.pubxml | 11 +- src/CrawlerSamples.ConsoleApp/RepoModel.cs | 26 ++ 23 files changed, 1131 insertions(+), 155 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/codex/prompts/review.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codex-review.yml create mode 100644 AGENTS.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 nuget.config delete mode 100644 src/CrawlerSamples.ConsoleApp/CarModel.cs create mode 100644 src/CrawlerSamples.ConsoleApp/RepoModel.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..765b447 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,280 @@ +# EditorConfig: https://editorconfig.org +# Applied by Visual Studio, VS Code, Rider, and `dotnet format`. + +root = true + +# ----------------------------- +# All files +# ----------------------------- +[*] +charset = utf-8 +end_of_line = crlf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +# ----------------------------- +# Markdown +# ----------------------------- +[*.md] +trim_trailing_whitespace = false + +# ----------------------------- +# YAML / GitHub Actions +# ----------------------------- +[*.{yml,yaml}] +indent_size = 2 + +# ----------------------------- +# JSON +# ----------------------------- +[*.json] +indent_size = 2 + +# ----------------------------- +# XML / MSBuild project files +# ----------------------------- +[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}] +indent_style = tab +indent_size = 4 + +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_style = tab +indent_size = 4 + +[*.{sln,DotSettings}] +indent_style = tab +indent_size = 4 + +# ----------------------------- +# Shell / scripts +# ----------------------------- +[*.{sh,ps1,cmd,bat}] +end_of_line = lf +indent_size = 2 + +[*.{cmd,bat,ps1}] +end_of_line = crlf + +# ----------------------------- +# C# source +# ----------------------------- +[*.cs] +indent_style = space +indent_size = 4 + +#### .NET coding conventions #### +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# this. / Me. preferences +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Language keywords vs BCL types +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion +dotnet_style_readonly_field = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_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 + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion + +# Null checking +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_null_check_over_type_check = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# coding conventions #### +# var preferences — prefer explicit types (matches existing sample style) +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:suggestion +csharp_style_var_elsewhere = false:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion + +# Null checking +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:silent +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion + +# Code-block preferences (Allman / new-line braces — matches this repo) +csharp_prefer_braces = true:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = false:suggestion +csharp_style_prefer_primary_constructors = false: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_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:suggestion + +# whether to prefer new() without type when type is apparent +dotnet_style_prefer_collection_expression = when_types_exactly_match:suggestion + +#### C# formatting rules #### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents_when_block = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_after_comma = true +csharp_space_before_comma = false +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true + +#### Naming conventions #### +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +dotnet_naming_style.camel_case_style.capitalization = camel_case +dotnet_naming_style.i_prefix_style.required_prefix = I +dotnet_naming_style.i_prefix_style.capitalization = pascal_case +dotnet_naming_style.underscore_camel_case.required_prefix = _ +dotnet_naming_style.underscore_camel_case.capitalization = camel_case + +# Interfaces: IPascalCase +dotnet_naming_rule.interface_should_be_begins_with_i.severity = true +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface_symbols +dotnet_naming_rule.interface_should_be_begins_with_i.style = i_prefix_style +dotnet_naming_symbols.interface_symbols.applicable_kinds = interface +dotnet_naming_symbols.interface_symbols.applicable_accessibilities = * +dotnet_naming_symbols.interface_symbols.required_modifiers = + +# Types / non-field members: PascalCase +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types_and_namespaces +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.types_and_namespaces.applicable_kinds = class,struct,interface,enum,delegate,namespace +dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = * + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.non_field_members.applicable_kinds = property,event,method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = * + +# Constants: PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +# Private / internal instance fields: _camelCase +dotnet_naming_rule.private_fields_should_be_underscore_camel.severity = suggestion +dotnet_naming_rule.private_fields_should_be_underscore_camel.symbols = private_fields +dotnet_naming_rule.private_fields_should_be_underscore_camel.style = underscore_camel_case +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private,private_protected,protected,internal,protected_internal +dotnet_naming_symbols.private_fields.required_modifiers = + +# Static fields (non-const): PascalCase +dotnet_naming_rule.static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_pascal_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.applicable_accessibilities = * +dotnet_naming_symbols.static_fields.required_modifiers = static + +# Local variables / parameters: camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter,local +dotnet_naming_symbols.locals_and_parameters.applicable_accessibilities = * diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..faf8844 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default owners for code review requests +* @VAllens diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..2f9f798 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,48 @@ +name: Bug report +description: Report a problem with the crawler sample +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for filing a bug. Please include enough detail to reproduce the issue. + - type: textarea + id: description + attributes: + label: Description + description: What happened? + validations: + required: true + - type: textarea + id: repro + attributes: + label: Steps to reproduce + description: Exact commands / actions + value: | + 1. + 2. + 3. + validations: + required: true + - type: input + id: sdk + attributes: + label: .NET SDK version + placeholder: e.g. 10.0.302 + validations: + required: true + - type: dropdown + id: os + attributes: + label: OS + options: + - Windows + - Linux + - macOS + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs / exception + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..63afa00 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,23 @@ +name: Feature request +description: Suggest an improvement to the sample +labels: ["enhancement"] +body: + - type: textarea + id: problem + attributes: + label: Problem / motivation + description: What problem would this solve for learners or maintainers? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed change + description: Describe the API / docs / sample change you want + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Optional diff --git a/.github/codex/prompts/review.md b/.github/codex/prompts/review.md new file mode 100644 index 0000000..ef0abf6 --- /dev/null +++ b/.github/codex/prompts/review.md @@ -0,0 +1,12 @@ +# Codex PR review rubric + +When reviewing a pull request in this repository, focus on: + +- Correctness bugs and exception paths +- Browser / page resource leaks +- Fragile HTML selectors and scraping breakage risks +- Security issues (secrets, unsafe deserialization, SSRF-like fetches) +- Missing docs / CI updates when runtime or packages change + +Be concise and specific. Prefer actionable findings with file paths. +Keep feedback proportional to the size of the PR. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..9740970 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: nuget + directory: /src/CrawlerSamples.ConsoleApp + schedule: + interval: weekly + open-pull-requests-limit: 5 + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6ec47c2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + branches: [master] + workflow_dispatch: + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "10.0.x" + + - name: Restore + run: dotnet restore CrawlerSamples.sln + + - name: Build + run: dotnet build CrawlerSamples.sln -c Release --no-restore + + - name: Publish (linux-x64 smoke) + if: runner.os == 'Linux' + run: > + dotnet publish src/CrawlerSamples.ConsoleApp/CrawlerSamples.ConsoleApp.csproj + -c Release + -r linux-x64 + --self-contained false + -o artifacts/linux-x64 + --no-restore + + - name: Upload publish artifact + if: runner.os == 'Linux' + uses: actions/upload-artifact@v4 + with: + name: crawler-samples-linux-x64 + path: artifacts/linux-x64 + if-no-files-found: error + retention-days: 7 diff --git a/.github/workflows/codex-review.yml b/.github/workflows/codex-review.yml new file mode 100644 index 0000000..bb46b3f --- /dev/null +++ b/.github/workflows/codex-review.yml @@ -0,0 +1,81 @@ +# Optional Codex PR review workflow. +# Enable by setting repository variable CODEX_REVIEW_ENABLED=true +# and adding repository secret OPENAI_API_KEY. +# +# This aligns with Codex for Open Source API-credit usage for +# pull request review / maintainer automation. +name: Codex PR Review + +on: + pull_request: + types: [opened, synchronize, reopened] + +concurrency: + group: codex-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + codex: + if: ${{ vars.CODEX_REVIEW_ENABLED == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + final_message: ${{ steps.run_codex.outputs.final-message }} + steps: + - name: Checkout PR merge commit + uses: actions/checkout@v5 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + persist-credentials: false + + - name: Pre-fetch base and head refs + env: + PR_BASE_REF: ${{ github.event.pull_request.base.ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + git fetch --no-tags origin \ + "$PR_BASE_REF" \ + "+refs/pull/$PR_NUMBER/head" + + - name: Run Codex + id: run_codex + uses: openai/codex-action@v1 + with: + openai-api-key: ${{ secrets.OPENAI_API_KEY }} + permission-profile: ":workspace" + prompt: | + Follow the review guidelines in .github/codex/prompts/review.md + + This is PR #${{ github.event.pull_request.number }} for ${{ github.repository }}. + + Review ONLY the changes introduced by the PR. Consider: + git log --oneline ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} + git diff --stat ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} + + Pull request title and body: + ---- + ${{ github.event.pull_request.title }} + ${{ github.event.pull_request.body }} + + post_feedback: + if: ${{ vars.CODEX_REVIEW_ENABLED == 'true' && needs.codex.outputs.final_message != '' }} + needs: codex + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Report Codex feedback + uses: actions/github-script@v7 + env: + CODEX_FINAL_MESSAGE: ${{ needs.codex.outputs.final_message }} + with: + github-token: ${{ github.token }} + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: process.env.CODEX_FINAL_MESSAGE, + }); diff --git a/.gitignore b/.gitignore index 3c4efe2..d5a18de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,68 +1,125 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache *.sln.docstates +*.env # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ -x64/ -x86/ + +[Dd]ebug/x64/ +[Dd]ebugPublic/x64/ +[Rr]elease/x64/ +[Rr]eleases/x64/ +bin/x64/ +obj/x64/ + +[Dd]ebug/x86/ +[Dd]ebugPublic/x86/ +[Rr]elease/x86/ +[Rr]eleases/x86/ +bin/x86/ +obj/x86/ + +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +[Aa][Rr][Mm]64[Ee][Cc]/ bld/ -[Bb]in/ [Oo]bj/ +[Oo]ut/ [Ll]og/ +[Ll]ogs/ + +# Build results on 'Bin' directories +**/[Bb]in/* +# Uncomment if you have tasks that rely on *.refresh files to move binaries +# (https://github.com/github/gitignore/pull/3736) +#!**/[Bb]in/*.refresh -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +*.trx -# NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml + +# Approval Tests result files +*.received.* # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c -# DNX +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core project.lock.json project.fragment.lock.json artifacts/ +.artifacts/ +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.idb +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log +*.tlog *.vspscc *.vssscc .builds @@ -90,6 +147,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -101,17 +161,28 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding add-in -.JustCode - # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + # NCrunch _NCrunch_* +.NCrunch_* .*crunch*.local.xml nCrunchTemp_* @@ -141,9 +212,9 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted -#*.pubxml +*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to @@ -153,13 +224,15 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore -**/packages/* +**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/packages/build/ +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -176,12 +249,15 @@ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt +*.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -192,9 +268,12 @@ ClientBin/ *.jfm *.pfx *.publishsettings -node_modules/ orleans.codegen.cs +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -209,15 +288,22 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf *.ldf +*.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -227,6 +313,7 @@ FakesAssemblies/ # Node.js Tools for Visual Studio .ntvs_analysis.dat +node_modules/ # Visual Studio 6 build log *.plg @@ -234,6 +321,17 @@ FakesAssemblies/ # Visual Studio 6 workspace options file *.opt +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts @@ -243,19 +341,89 @@ FakesAssemblies/ _Pvt_Extensions # Paket dependency manager -.paket/paket.exe +**/.paket/paket.exe paket-files/ # FAKE - F# Make -.fake/ +**/.fake/ -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ +# CodeRush personal settings +**/.cr/personal # Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc \ No newline at end of file +**/__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +#tools/** +#!tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog +MSBuild_Logs/ + +# AWS SAM Build and Temporary Artifacts folder +.aws-sam + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +**/.mfractor/ + +# Local History for Visual Studio +**/.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +**/.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..bb5066d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ +# Repository guidance for AI coding agents + +This repository is a .NET sample demonstrating Puppeteer Sharp + AngleSharp crawling. + +## Goals + +- Keep the sample small, readable, and runnable with `dotnet run` +- Prefer correctness and resource safety (browser/page disposal) over clever abstractions +- Preserve MIT licensing and public-sample intent + +## When changing code + +1. Run `dotnet build CrawlerSamples.sln -c Release` after edits +2. Do not add secrets, credentials, or private scrape targets +3. If HTML selectors change, update `CreateModelWithAngleSharp` and mention it in the PR +4. Keep README/CI docs in sync with target framework and package versions +5. Avoid expanding scope into a full crawler framework unless explicitly requested + +## CI expectations + +- GitHub Actions builds on push/PR to `master` +- Optional Codex PR review workflow runs only when `CODEX_REVIEW_ENABLED=true` diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f9d6cf7 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,66 @@ +# 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 behavior 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 behavior 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 behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement via the maintainer +contact details on [GitHub](https://github.com/VAllens). + +All complaints will be reviewed and investigated promptly and fairly. + +## 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]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0f7eecf --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to Crawler Samples + +Thanks for helping keep this sample useful for the .NET crawler / scraping community. + +## Development setup + +1. Install the [.NET 10 SDK](https://dotnet.microsoft.com/download) +2. Clone the repository +3. Restore and build: + +```bash +dotnet restore +dotnet build CrawlerSamples.sln -c Release +``` + +4. Run the sample: + +```bash +dotnet run --project src/CrawlerSamples.ConsoleApp +``` + +## Pull requests + +- Keep changes focused and documented in the PR description +- Prefer small PRs that are easy to review +- Update README / comments when selectors, target pages, or runtime requirements change +- Make sure `dotnet build CrawlerSamples.sln -c Release` succeeds locally +- Avoid committing secrets, local Chromium caches, or build outputs + +## Coding guidelines + +- Target `net10.0` and keep nullable reference types enabled +- Prefer explicit resource cleanup for browser / page objects +- Keep HTML selectors as stable as possible; document fragile CSS-module selectors +- Do not scrape private or authenticated content in this sample + +## Issues + +When filing a bug, include: + +- OS and .NET SDK version +- Package versions (`PuppeteerSharp`, `AngleSharp`) +- Exact exception / stack trace +- Whether Chromium downloaded successfully + +## Code of conduct + +This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). diff --git a/LICENSE b/LICENSE index b48df30..8345c25 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Darío Kondratiuk +Copyright (c) 2017-2026 Allen (VAllens) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 923db67..3161046 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,100 @@ # Crawler Samples -This is a `PuppeteerSharp` + `AngleSharp` crawler console app samples, used `C# 7.1` coding and dotnet core build. -# Useful links -* [Puppeteer Sharp](https://github.com/kblok/puppeteer-sharp) -* [AngleSharp](https://github.com/AngleSharp/AngleSharp) +[![CI](https://github.com/VAllens/CrawlerSamples/actions/workflows/ci.yml/badge.svg)](https://github.com/VAllens/CrawlerSamples/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![.NET](https://img.shields.io/badge/.NET-10.0-512BD4)](https://dotnet.microsoft.com/) +A small, practical **C#** sample that crawls a modern JavaScript-rendered page with [Puppeteer Sharp](https://github.com/hardkoded/puppeteer-sharp), then parses and extracts structured data with [AngleSharp](https://github.com/AngleSharp/AngleSharp). -# Usage -Open this sample project in the `Visual Studio 2017` and press **`F5`** key run it. -### Or -Open this sample project in the `Visual Studio 2017` and enter **`Build(B) --> Publish(H)`** in the top toolbar. +The demo targets the public [.NET organization repositories](https://github.com/orgs/dotnet/repositories) page on GitHub and prints the extracted repository list as JSON. -Go to `build\win\netcoreapp2.0\x64` or `build\win\netcoreapp2.0\x86` folder, run `CrawlerSamples.ConsoleApp.exe` here. +## Why this sample exists +Many real-world pages are no longer static HTML. This repository shows a maintainable pattern for: -# Result picture -Sample Snapshoot +1. Rendering dynamic pages in a headless Chromium browser (`PuppeteerSharp`) +2. Parsing the resulting DOM with a standards-based HTML parser (`AngleSharp`) +3. Mapping DOM nodes into typed models and serializing them with `System.Text.Json` -# Note -For the first time, it will download the `download-Win64-536395.zip` portable installation package from the network to the .local-chromium directory of the current program. +It is intended as a learning / reference project for .NET developers building crawlers, scrapers, or HTML extraction tools. -It will some time to wait here. +## Requirements -If a network problem occurs during downloading, which results in download failure, it throws an exception and exits the program. +- [.NET 10 SDK](https://dotnet.microsoft.com/download) +- Network access on first run (downloads a Chromium revision used by Puppeteer Sharp) +## Quick start -# Author -Author: [Allen](http://vallen.cnblogs.com) +```bash +dotnet restore +dotnet run --project src/CrawlerSamples.ConsoleApp +``` -# License -MIT +Or open `CrawlerSamples.sln` in Visual Studio / VS Code / Rider and run the console app. + +### Publish (optional) + +```bash +dotnet publish src/CrawlerSamples.ConsoleApp -c Release -r win-x64 --self-contained true -o build/win/net10.0/x64 +``` + +You can also use the included publish profiles under `src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/`. + +## Sample output + +The app prints a JSON array of repository models, for example: + +```json +[ + { + "url": "/dotnet/runtime", + "visibility": "Public", + "title": "runtime", + "description": ".NET is a cross-platform runtime...", + "language": "C#", + "license": "MIT" + } +] +``` + +Screenshot: + +Sample snapshot + +## Project layout + +```text +CrawlerSamples.sln +src/CrawlerSamples.ConsoleApp/ + Program.cs # Crawl + parse flow + RepoModel.cs # Extracted model + JSON source generation +``` + +## Notes + +- On first run, Puppeteer Sharp downloads a Chromium package into a local cache. This can take a while depending on your network. +- If the Chromium download fails, the process exits with an exception. +- GitHub's UI markup can change. If selectors stop matching, update them in `CreateModelWithAngleSharp`. +- The sample waits for a key press only in interactive terminals; redirected / CI environments exit immediately after printing results. + +## Useful links + +- [Puppeteer Sharp](https://github.com/hardkoded/puppeteer-sharp) +- [AngleSharp](https://github.com/AngleSharp/AngleSharp) +- [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). Please open issues and pull requests — CI builds on every PR. + +## Security + +See [SECURITY.md](SECURITY.md). + +## Author + +[Allen (VAllens)](https://github.com/VAllens) + +## License + +[MIT](LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..df5fdb4 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Supported versions + +This is a sample / educational repository. Security fixes are applied on the latest `master` branch only. + +## Reporting a vulnerability + +Please **do not** open a public issue for security-sensitive reports. + +Instead, email the maintainer via the contact details on the [GitHub profile](https://github.com/VAllens), or open a private [GitHub Security Advisory](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) if available for this repository. + +Include: + +- A short description of the issue +- Steps to reproduce +- Impact assessment (if known) +- Any suggested fix + +We aim to acknowledge reports within 7 days. + +## Scope notes + +This sample launches a headless browser and fetches public web content. When adapting it for production: + +- Respect target site `robots.txt`, terms of service, and rate limits +- Do not store or log credentials in source control +- Pin package versions and keep dependencies updated (Dependabot is enabled) +- Treat HTML from the network as untrusted input diff --git a/SampleSnapshoot.png b/SampleSnapshoot.png index 73e597882b134c983db60bb14fcde51e0daecfb3..20cb1601f6632452840cab2ac89f439e94fcba7d 100644 GIT binary patch literal 52533 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 XEc|))37!Z4>)z;&nLh5;<3IlwlZf>W diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..4d736c1 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/CrawlerSamples.ConsoleApp/CarModel.cs b/src/CrawlerSamples.ConsoleApp/CarModel.cs deleted file mode 100644 index 07f8549..0000000 --- a/src/CrawlerSamples.ConsoleApp/CarModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace CrawlerSamples -{ - public class CarModel - { - public virtual string Title { get; set; } - - public virtual string ImageUrl { get; set; } - - public virtual string ProductUrl { get; set; } - - public virtual string OriginalPrice { get; set; } - - public virtual string PresentPrice { get; set; } - - public virtual string OrdersNumber { get; set; } - - public virtual string Tip { get; set; } - } -} \ No newline at end of file diff --git a/src/CrawlerSamples.ConsoleApp/CrawlerSamples.ConsoleApp.csproj b/src/CrawlerSamples.ConsoleApp/CrawlerSamples.ConsoleApp.csproj index c85661b..07ebc46 100644 --- a/src/CrawlerSamples.ConsoleApp/CrawlerSamples.ConsoleApp.csproj +++ b/src/CrawlerSamples.ConsoleApp/CrawlerSamples.ConsoleApp.csproj @@ -1,16 +1,18 @@  - - Exe - netcoreapp3.0 - 7.1 - CrawlerSamples - + + Exe + net10.0 + CrawlerSamples + enable + disable + latest + true + - - - - - + + + + diff --git a/src/CrawlerSamples.ConsoleApp/Program.cs b/src/CrawlerSamples.ConsoleApp/Program.cs index e5c46e5..d469dcc 100644 --- a/src/CrawlerSamples.ConsoleApp/Program.cs +++ b/src/CrawlerSamples.ConsoleApp/Program.cs @@ -1,113 +1,156 @@ -/* +/* * This is a Puppeteer+AngleSharp crawler console app samples */ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; using AngleSharp; using AngleSharp.Dom; +using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; -using Newtonsoft.Json; using PuppeteerSharp; +using PuppeteerSharp.BrowserData; +using System; +using System.Linq; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Threading.Tasks; namespace CrawlerSamples { - internal class Program + internal static class Program { - private const string Url = "https://store.mall.autohome.com.cn/83106681.html"; - private const int ChromiumRevision = BrowserFetcher.DefaultRevision; + private const string Url = "https://github.com/orgs/dotnet/repositories"; private static async Task Main(string[] args) { - //Download chromium browser revision package - await new BrowserFetcher().DownloadAsync(ChromiumRevision); + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + // Download chromium browser revision package + await new BrowserFetcher().DownloadAsync(Chrome.DefaultBuildId); - //Test AngleSharp await TestAngleSharp(); - Console.ReadKey(); + if (ShouldWaitForExit()) + { + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(intercept: true); + } + } + + private static bool ShouldWaitForExit() + { + return Environment.UserInteractive + && !Console.IsInputRedirected + && !Console.IsOutputRedirected; } private static async Task TestAngleSharp() { /* * Used AngleSharp loading of HTML document - * TODO: Used WithJavaScript function need install AngleSharp.Scripting.Javascript nuget package - * Note: that JavaScripts support is an experimental and does not support complex JavaScripts code. + * TODO: To enable JS evaluation, install the AngleSharp.Js nuget package + * (AngleSharp.Scripting.Javascript is deprecated and replaced by AngleSharp.Js) + * and call WithJs() on the configuration. + * Note: AngleSharp.Js (via Jint) now supports modern ECMAScript (ES2015–ES2025) and can run + * libraries such as jQuery / React 16 / Bootstrap 5 in its test suite. For many + * real-world pages you typically also need AngleSharp.Io, AngleSharp.Css, and WithEventLoop(). + * It is still not a full browser: DOM/browser API coverage and heavy SPA apps can fail, + * and Jint is an interpreter (slower than Chromium). Prefer PuppeteerSharp for complex sites. */ - //IConfiguration config = Configuration.Default.WithDefaultLoader(); + //IConfiguration config = Configuration.Default + // .WithDefaultLoader(new LoaderOptions { IsResourceLoadingEnabled = true }) + // //.WithCss() + // .WithJs() + // .WithEventLoop(); //IBrowsingContext context = BrowsingContext.New(config); //IDocument document = await context.OpenAsync(Url); - //Used PuppeteerSharp loading of HTML document - var htmlString = await TestPuppeteerSharp(); + // Used PuppeteerSharp loading of HTML document + string htmlString = await TestPuppeteerSharp(); /* * Parsing of HTML document string */ - var context = BrowsingContext.New(Configuration.Default); - var parser = context.GetService(); - var document = parser.ParseDocument(htmlString); + IBrowsingContext context = BrowsingContext.New(Configuration.Default); + IHtmlParser? parser = context.GetService(); + if (parser == null) + { + throw new InvalidOperationException($"Failed to get {nameof(IHtmlParser)} service."); + } - //Selector carbox element list - var carboxList = document.QuerySelectorAll("div.shop-content div.content div.list li.carbox"); + IHtmlDocument document = parser.ParseDocument(htmlString); - var carModelList = new List(); - foreach (var carbox in carboxList) + // Selector repository element list + // Note: GitHub UI markup changes over time; keep selectors under review when scraping breaks. + Type listItemType = typeof(IHtmlListItemElement); + IHtmlCollection repoElementList = document.QuerySelectorAll("ul[data-listview-component='items-list'] > li") + .Where(x => listItemType.IsInstanceOfType(x)) + .Cast() + .ToCollection(); + + RepoModel[] repoModels = new RepoModel[repoElementList.Length]; + for (int i = 0; i < repoElementList.Length; i++) { - //Parsing and converting to the car model object. - var model = CreateModelWithAngleSharp(carbox); - carModelList.Add(model); - - //Printing to console windows - var jsonString = JsonConvert.SerializeObject(model); - Console.WriteLine(jsonString); - Console.WriteLine(); + // Parsing and converting to the repository model object. + repoModels[i] = CreateModelWithAngleSharp(repoElementList[i]); } - Console.WriteLine("Total count:" + carModelList.Count); + // Printing to console windows + JsonSerializerOptions options = new JsonSerializerOptions + { + TypeInfoResolver = CustomJsonSerializerContext.Default, + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + string jsonString = JsonSerializer.Serialize(repoModels, options); + Console.WriteLine(jsonString); + Console.WriteLine(); + Console.WriteLine("Total count: " + repoModels.Length); } private static async Task TestPuppeteerSharp() { - //Enabled headless option - var launchOptions = new LaunchOptions { Headless = true }; - //Starting headless browser - var browser = await Puppeteer.LaunchAsync(launchOptions); - - //Get all(default) pages - var pages = await browser.PagesAsync(); - //Get first page or new tab page - var firstPage = pages.Length > 0 ? pages[0]: await browser.NewPageAsync(); - //Request URL to get the page - await firstPage.GoToAsync(Url); - - //Get and return the HTML content of the page - var htmlString = await firstPage.GetContentAsync(); - - #region Dispose resources - //Close tab page - await firstPage.CloseAsync(); - - //Close headless browser, all pages will be closed here. - await browser.CloseAsync(); - #endregion - - return htmlString; + // Enabled headless option + LaunchOptions launchOptions = new LaunchOptions { Headless = true }; + // Starting headless browser + await using IBrowser browser = await Puppeteer.LaunchAsync(launchOptions).ConfigureAwait(false); + + // Get all(default) pages + IPage[] pages = await browser.PagesAsync().ConfigureAwait(false); + // Get first page or new tab page + IPage firstPage = pages.Length > 0 ? pages[0] : await browser.NewPageAsync().ConfigureAwait(false); + + try + { + // Request URL to get the page + await firstPage.GoToAsync(Url, WaitUntilNavigation.Networkidle2).ConfigureAwait(false); + // Get and return the HTML content of the page + return await firstPage.GetContentAsync().ConfigureAwait(false); + } + finally + { + if (!firstPage.IsClosed) + { + await firstPage.CloseAsync().ConfigureAwait(false); + } + } } - private static CarModel CreateModelWithAngleSharp(IParentNode node) + private static RepoModel CreateModelWithAngleSharp(IHtmlListItemElement repoItem) { - var model = new CarModel + return new RepoModel { - Title = node.QuerySelector("a div.carbox-title").TextContent, - ImageUrl = node.QuerySelector("a div.carbox-carimg img").GetAttribute("src"), - ProductUrl = node.QuerySelector("a").GetAttribute("href"), - Tip = node.QuerySelector("a div.carbox-tip").TextContent, - OrdersNumber = node.QuerySelector("a div.carbox-number span").TextContent + Url = NormalizeText(repoItem.QuerySelector("div[data-listview-item-title-container] > h4 > a")?.GetAttribute("href")), + Title = NormalizeText(repoItem.QuerySelector("div[data-listview-item-title-container] > h4 > a > span")?.TextContent), + Description = NormalizeText(repoItem.QuerySelector("div.repos-list-description > div")?.TextContent), + Visibility = NormalizeText(repoItem.QuerySelector("span[data-listview-item-visibility-label]")?.TextContent), + // CSS-module class prefixes are fragile; prefer data-* attributes when available. + Language = NormalizeText(repoItem.QuerySelector("span[class^='ReposListItem-module__PrimaryLanguageName']")?.TextContent), + License = NormalizeText(repoItem.QuerySelector("div[class^='ReposListItem-module__IconLabel']")?.TextContent) }; - - return model; } + + private static string? NormalizeText(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } } diff --git a/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin64ReleaseToLocalFolder.pubxml b/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin64ReleaseToLocalFolder.pubxml index f113d4a..c2102a4 100644 --- a/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin64ReleaseToLocalFolder.pubxml +++ b/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin64ReleaseToLocalFolder.pubxml @@ -1,16 +1,17 @@  FileSystem Release Any CPU - netcoreapp3.0 - ../../build/win/netcoreapp3.0/x64 + net10.0 + ../../build/win/net10.0/x64 win-x64 true - <_IsPortable>false + true + true - \ No newline at end of file + diff --git a/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin86ReleaseToLocalFolder.pubxml b/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin86ReleaseToLocalFolder.pubxml index e6ff13a..b01b002 100644 --- a/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin86ReleaseToLocalFolder.pubxml +++ b/src/CrawlerSamples.ConsoleApp/Properties/PublishProfiles/PublishWin86ReleaseToLocalFolder.pubxml @@ -1,16 +1,17 @@  FileSystem Release Any CPU - netcoreapp3.0 - ../../build/win/netcoreapp3.0/x86 + net10.0 + ../../build/win/net10.0/x86 win-x86 true - <_IsPortable>false + true + true - \ No newline at end of file + diff --git a/src/CrawlerSamples.ConsoleApp/RepoModel.cs b/src/CrawlerSamples.ConsoleApp/RepoModel.cs new file mode 100644 index 0000000..bbe2fde --- /dev/null +++ b/src/CrawlerSamples.ConsoleApp/RepoModel.cs @@ -0,0 +1,26 @@ +using System.Text.Json.Serialization; + +namespace CrawlerSamples +{ + internal readonly record struct RepoModel + { + public string? Url { get; init; } + + public string? Visibility { get; init; } + + public string? Title { get; init; } + + public string? Description { get; init; } + + public string? Language { get; init; } + + public string? License { get; init; } + } + + [JsonSerializable(typeof(RepoModel))] + [JsonSerializable(typeof(RepoModel[]))] + internal partial class CustomJsonSerializerContext : JsonSerializerContext + { + + } +} From 66d8876176937aba7960d5ba53a37d385656a441 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:32:40 -0700 Subject: [PATCH 2/9] fix: use --use-current-runtime for cross-platform publish in CI (#8) --- .github/workflows/ci.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ec47c2..3eaa5ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,26 +30,23 @@ jobs: dotnet-version: "10.0.x" - name: Restore - run: dotnet restore CrawlerSamples.sln + run: dotnet restore CrawlerSamples.sln --use-current-runtime - name: Build run: dotnet build CrawlerSamples.sln -c Release --no-restore - - name: Publish (linux-x64 smoke) - if: runner.os == 'Linux' + - name: Publish run: > dotnet publish src/CrawlerSamples.ConsoleApp/CrawlerSamples.ConsoleApp.csproj -c Release - -r linux-x64 - --self-contained false - -o artifacts/linux-x64 - --no-restore + --use-current-runtime + --self-contained + -o artifacts/publish - name: Upload publish artifact - if: runner.os == 'Linux' uses: actions/upload-artifact@v4 with: - name: crawler-samples-linux-x64 - path: artifacts/linux-x64 + name: crawler-samples-${{ matrix.os }} + path: artifacts/publish if-no-files-found: error retention-days: 7 From 937826be5821ade360fc79e2e9629898f50d7c44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:43:05 -0700 Subject: [PATCH 3/9] Bump actions/upload-artifact from 4 to 7 (#7) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3eaa5ce..83cb73b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: -o artifacts/publish - name: Upload publish artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: crawler-samples-${{ matrix.os }} path: artifacts/publish From 8efd755fbeb2f5750322406c36cb33c055635fb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:43:42 -0700 Subject: [PATCH 4/9] Bump actions/github-script from 7 to 9 (#6) Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 9. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v9) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codex-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codex-review.yml b/.github/workflows/codex-review.yml index bb46b3f..fedaa90 100644 --- a/.github/workflows/codex-review.yml +++ b/.github/workflows/codex-review.yml @@ -67,7 +67,7 @@ jobs: pull-requests: write steps: - name: Report Codex feedback - uses: actions/github-script@v7 + uses: actions/github-script@v9 env: CODEX_FINAL_MESSAGE: ${{ needs.codex.outputs.final_message }} with: From 75e86000f9c3bde0c40c5bb25a560582633d22c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:44:04 -0700 Subject: [PATCH 5/9] Bump actions/checkout from 5 to 7 (#5) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/codex-review.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83cb73b..0306bd9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v7 - name: Setup .NET uses: actions/setup-dotnet@v4 diff --git a/.github/workflows/codex-review.yml b/.github/workflows/codex-review.yml index fedaa90..18d7a22 100644 --- a/.github/workflows/codex-review.yml +++ b/.github/workflows/codex-review.yml @@ -24,7 +24,7 @@ jobs: final_message: ${{ steps.run_codex.outputs.final-message }} steps: - name: Checkout PR merge commit - uses: actions/checkout@v5 + uses: actions/checkout@v7 with: ref: refs/pull/${{ github.event.pull_request.number }}/merge persist-credentials: false From 37de28ac87219e1364fb9103b3e7b2bf54b4b4b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 04:44:18 -0700 Subject: [PATCH 6/9] Bump actions/setup-dotnet from 4 to 6 (#4) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4 to 6. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0306bd9..2d55b06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v7 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v6 with: dotnet-version: "10.0.x" From d48ba306121fc1b73c89f6c80a13da1c62aca0c6 Mon Sep 17 00:00:00 2001 From: Allen Cai Date: Tue, 11 Aug 2026 20:22:20 +0800 Subject: [PATCH 7/9] Update branch references from master to main in CI and documentation --- .github/workflows/ci.yml | 4 ++-- AGENTS.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- build/win/.gitignore | 3 --- 5 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 build/win/.gitignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d55b06..e33267c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [master] + branches: [main] pull_request: - branches: [master] + branches: [main] workflow_dispatch: concurrency: diff --git a/AGENTS.md b/AGENTS.md index bb5066d..7dedc46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,5 +18,5 @@ This repository is a .NET sample demonstrating Puppeteer Sharp + AngleSharp craw ## CI expectations -- GitHub Actions builds on push/PR to `master` +- GitHub Actions builds on push/PR to `main` - Optional Codex PR review workflow runs only when `CODEX_REVIEW_ENABLED=true` diff --git a/README.md b/README.md index 3161046..fab405c 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ The app prints a JSON array of repository models, for example: Screenshot: -Sample snapshot +Sample snapshot ## Project layout diff --git a/SECURITY.md b/SECURITY.md index df5fdb4..7cbe720 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Supported versions -This is a sample / educational repository. Security fixes are applied on the latest `master` branch only. +This is a sample / educational repository. Security fixes are applied on the latest `main` branch only. ## Reporting a vulnerability diff --git a/build/win/.gitignore b/build/win/.gitignore deleted file mode 100644 index 3ad085d..0000000 --- a/build/win/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Ignore everything in this directory -* -# Except this file !.gitignore \ No newline at end of file From 138ead0745ef281c6fac035c528791bab2c659ab Mon Sep 17 00:00:00 2001 From: Allen Cai Date: Tue, 11 Aug 2026 05:24:31 -0700 Subject: [PATCH 8/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/CrawlerSamples.ConsoleApp/Program.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/CrawlerSamples.ConsoleApp/Program.cs b/src/CrawlerSamples.ConsoleApp/Program.cs index d469dcc..88ef2fb 100644 --- a/src/CrawlerSamples.ConsoleApp/Program.cs +++ b/src/CrawlerSamples.ConsoleApp/Program.cs @@ -81,10 +81,9 @@ private static async Task TestAngleSharp() // Selector repository element list // Note: GitHub UI markup changes over time; keep selectors under review when scraping breaks. - Type listItemType = typeof(IHtmlListItemElement); - IHtmlCollection repoElementList = document.QuerySelectorAll("ul[data-listview-component='items-list'] > li") - .Where(x => listItemType.IsInstanceOfType(x)) - .Cast() + IHtmlCollection repoElementList = document + .QuerySelectorAll("ul[data-listview-component='items-list'] > li") + .OfType() .ToCollection(); RepoModel[] repoModels = new RepoModel[repoElementList.Length]; From 0fac298ff2c835249090041f65dd6e493cb547e7 Mon Sep 17 00:00:00 2001 From: Allen Cai Date: Tue, 11 Aug 2026 20:31:40 +0800 Subject: [PATCH 9/9] Update the CI workflow to support macOS environments --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e33267c..ba14dde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Checkout