Full Code of SeleniumHQ/docker-selenium for AI

trunk 40ed786ec40c cached
2070 files
3.7 MB
1.1M tokens
204 symbols
1 requests
Download .txt
Showing preview only (4,260K chars total). Download the full file or copy to clipboard to get everything.
Repository: SeleniumHQ/docker-selenium
Branch: trunk
Commit: 40ed786ec40c
Files: 2070
Total size: 3.7 MB

Directory structure:
gitextract_11114r06/

├── .circleci/
│   └── config.bak
├── .editorconfig
├── .ffmpeg/
│   └── Dockerfile
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_proposal.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   └── get-latest-upstream/
│   │       └── action.yml
│   ├── dependabot.yml
│   ├── label-commenter-config.yml
│   └── workflows/
│       ├── build-ffmpeg.yml
│       ├── build-test.yml
│       ├── create-changelog-pr.yml
│       ├── deploy.yml
│       ├── docker-test.yml
│       ├── helm-chart-release.yml
│       ├── helm-chart-test.yml
│       ├── k8s-scaling-test.yml
│       ├── label-commenter.yml
│       ├── lock.yml
│       ├── nightly.yml
│       ├── release-all-browser-versions.yml
│       ├── release-chrome-for-testing-versions.yml
│       ├── release-chrome-versions.yml
│       ├── release-edge-versions.yml
│       ├── release-firefox-versions.yml
│       ├── release-preparation.yml
│       ├── rerun-failed.yml
│       ├── scan-dockerfile.yml
│       ├── update-chart-readme.yml
│       └── update-dev-beta-browser-images.yml
├── .gitignore
├── .gitpod.yml
├── .keda/
│   ├── README.md
│   ├── results_test_k8s_autoscaling_deployment_count.md
│   ├── results_test_k8s_autoscaling_deployment_count_in_chaos.md
│   ├── results_test_k8s_autoscaling_deployment_count_with_node_max_sessions.md
│   ├── results_test_k8s_autoscaling_job_count_strategy_default.md
│   ├── results_test_k8s_autoscaling_job_count_strategy_default_in_chaos.md
│   ├── results_test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions.md
│   └── scalers/
│       ├── selenium-grid-scaler.md
│       ├── selenium_grid_scaler.go
│       └── selenium_grid_scaler_test.go
├── Base/
│   ├── Dockerfile
│   ├── check-grid.sh
│   ├── entry_point.sh
│   ├── handle_heap_dump.sh
│   ├── mask
│   └── supervisord.conf
├── CHANGELOG/
│   ├── 4.41.0/
│   │   ├── chrome-for-testing_113.md
│   │   ├── chrome-for-testing_114.md
│   │   ├── chrome-for-testing_115.md
│   │   ├── chrome-for-testing_116.md
│   │   ├── chrome-for-testing_117.md
│   │   ├── chrome-for-testing_118.md
│   │   ├── chrome-for-testing_119.md
│   │   ├── chrome-for-testing_120.md
│   │   ├── chrome-for-testing_121.md
│   │   ├── chrome-for-testing_122.md
│   │   ├── chrome-for-testing_123.md
│   │   ├── chrome-for-testing_124.md
│   │   ├── chrome-for-testing_125.md
│   │   ├── chrome-for-testing_126.md
│   │   ├── chrome-for-testing_127.md
│   │   ├── chrome-for-testing_128.md
│   │   ├── chrome-for-testing_129.md
│   │   ├── chrome-for-testing_130.md
│   │   ├── chrome-for-testing_131.md
│   │   ├── chrome-for-testing_132.md
│   │   ├── chrome-for-testing_133.md
│   │   ├── chrome-for-testing_134.md
│   │   ├── chrome-for-testing_135.md
│   │   ├── chrome-for-testing_136.md
│   │   ├── chrome-for-testing_137.md
│   │   ├── chrome-for-testing_138.md
│   │   ├── chrome-for-testing_139.md
│   │   ├── chrome-for-testing_140.md
│   │   ├── chrome-for-testing_141.md
│   │   ├── chrome-for-testing_142.md
│   │   ├── chrome-for-testing_143.md
│   │   ├── chrome-for-testing_144.md
│   │   ├── chrome-for-testing_145.md
│   │   ├── chrome_100.md
│   │   ├── chrome_101.md
│   │   ├── chrome_102.md
│   │   ├── chrome_103.md
│   │   ├── chrome_104.md
│   │   ├── chrome_105.md
│   │   ├── chrome_106.md
│   │   ├── chrome_107.md
│   │   ├── chrome_108.md
│   │   ├── chrome_109.md
│   │   ├── chrome_110.md
│   │   ├── chrome_111.md
│   │   ├── chrome_112.md
│   │   ├── chrome_113.md
│   │   ├── chrome_114.md
│   │   ├── chrome_115.md
│   │   ├── chrome_116.md
│   │   ├── chrome_117.md
│   │   ├── chrome_118.md
│   │   ├── chrome_119.md
│   │   ├── chrome_120.md
│   │   ├── chrome_121.md
│   │   ├── chrome_122.md
│   │   ├── chrome_123.md
│   │   ├── chrome_124.md
│   │   ├── chrome_125.md
│   │   ├── chrome_126.md
│   │   ├── chrome_127.md
│   │   ├── chrome_128.md
│   │   ├── chrome_129.md
│   │   ├── chrome_130.md
│   │   ├── chrome_131.md
│   │   ├── chrome_132.md
│   │   ├── chrome_133.md
│   │   ├── chrome_134.md
│   │   ├── chrome_135.md
│   │   ├── chrome_136.md
│   │   ├── chrome_137.md
│   │   ├── chrome_138.md
│   │   ├── chrome_139.md
│   │   ├── chrome_140.md
│   │   ├── chrome_141.md
│   │   ├── chrome_142.md
│   │   ├── chrome_143.md
│   │   ├── chrome_144.md
│   │   ├── chrome_145.md
│   │   ├── chrome_95.md
│   │   ├── chrome_96.md
│   │   ├── chrome_97.md
│   │   ├── chrome_98.md
│   │   ├── chrome_99.md
│   │   ├── edge_114.md
│   │   ├── edge_115.md
│   │   ├── edge_116.md
│   │   ├── edge_117.md
│   │   ├── edge_118.md
│   │   ├── edge_119.md
│   │   ├── edge_120.md
│   │   ├── edge_121.md
│   │   ├── edge_122.md
│   │   ├── edge_123.md
│   │   ├── edge_124.md
│   │   ├── edge_125.md
│   │   ├── edge_126.md
│   │   ├── edge_127.md
│   │   ├── edge_128.md
│   │   ├── edge_129.md
│   │   ├── edge_130.md
│   │   ├── edge_131.md
│   │   ├── edge_132.md
│   │   ├── edge_133.md
│   │   ├── edge_134.md
│   │   ├── edge_135.md
│   │   ├── edge_136.md
│   │   ├── edge_137.md
│   │   ├── edge_138.md
│   │   ├── edge_139.md
│   │   ├── edge_140.md
│   │   ├── edge_141.md
│   │   ├── edge_142.md
│   │   ├── edge_143.md
│   │   ├── edge_144.md
│   │   ├── edge_145.md
│   │   ├── firefox_100.md
│   │   ├── firefox_101.md
│   │   ├── firefox_102.md
│   │   ├── firefox_103.md
│   │   ├── firefox_104.md
│   │   ├── firefox_105.md
│   │   ├── firefox_106.md
│   │   ├── firefox_107.md
│   │   ├── firefox_108.md
│   │   ├── firefox_109.md
│   │   ├── firefox_110.md
│   │   ├── firefox_111.md
│   │   ├── firefox_112.md
│   │   ├── firefox_113.md
│   │   ├── firefox_114.md
│   │   ├── firefox_115.md
│   │   ├── firefox_116.md
│   │   ├── firefox_117.md
│   │   ├── firefox_118.md
│   │   ├── firefox_119.md
│   │   ├── firefox_120.md
│   │   ├── firefox_121.md
│   │   ├── firefox_122.md
│   │   ├── firefox_123.md
│   │   ├── firefox_124.md
│   │   ├── firefox_125.md
│   │   ├── firefox_126.md
│   │   ├── firefox_127.md
│   │   ├── firefox_128.md
│   │   ├── firefox_129.md
│   │   ├── firefox_130.md
│   │   ├── firefox_131.md
│   │   ├── firefox_132.md
│   │   ├── firefox_133.md
│   │   ├── firefox_134.md
│   │   ├── firefox_135.md
│   │   ├── firefox_136.md
│   │   ├── firefox_137.md
│   │   ├── firefox_138.md
│   │   ├── firefox_139.md
│   │   ├── firefox_140.md
│   │   ├── firefox_141.md
│   │   ├── firefox_142.md
│   │   ├── firefox_143.md
│   │   ├── firefox_144.md
│   │   ├── firefox_145.md
│   │   ├── firefox_146.md
│   │   ├── firefox_147.md
│   │   ├── firefox_148.md
│   │   ├── firefox_98.md
│   │   └── firefox_99.md
│   ├── README.md
│   ├── archived/
│   │   ├── 4.28.1/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.29.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.30.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.31.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.32.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.33.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.34.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.35.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.36.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.37.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_143.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.38.0/
│   │   │   ├── chrome-for-testing_113.md
│   │   │   ├── chrome-for-testing_114.md
│   │   │   ├── chrome-for-testing_115.md
│   │   │   ├── chrome-for-testing_116.md
│   │   │   ├── chrome-for-testing_117.md
│   │   │   ├── chrome-for-testing_118.md
│   │   │   ├── chrome-for-testing_119.md
│   │   │   ├── chrome-for-testing_120.md
│   │   │   ├── chrome-for-testing_121.md
│   │   │   ├── chrome-for-testing_122.md
│   │   │   ├── chrome-for-testing_123.md
│   │   │   ├── chrome-for-testing_124.md
│   │   │   ├── chrome-for-testing_125.md
│   │   │   ├── chrome-for-testing_126.md
│   │   │   ├── chrome-for-testing_127.md
│   │   │   ├── chrome-for-testing_128.md
│   │   │   ├── chrome-for-testing_129.md
│   │   │   ├── chrome-for-testing_130.md
│   │   │   ├── chrome-for-testing_131.md
│   │   │   ├── chrome-for-testing_132.md
│   │   │   ├── chrome-for-testing_133.md
│   │   │   ├── chrome-for-testing_134.md
│   │   │   ├── chrome-for-testing_135.md
│   │   │   ├── chrome-for-testing_136.md
│   │   │   ├── chrome-for-testing_137.md
│   │   │   ├── chrome-for-testing_138.md
│   │   │   ├── chrome-for-testing_139.md
│   │   │   ├── chrome-for-testing_140.md
│   │   │   ├── chrome-for-testing_141.md
│   │   │   ├── chrome-for-testing_142.md
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_141.md
│   │   │   ├── chrome_142.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── edge_141.md
│   │   │   ├── edge_142.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_143.md
│   │   │   ├── firefox_144.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.39.0/
│   │   │   ├── chrome-for-testing_113.md
│   │   │   ├── chrome-for-testing_114.md
│   │   │   ├── chrome-for-testing_115.md
│   │   │   ├── chrome-for-testing_116.md
│   │   │   ├── chrome-for-testing_117.md
│   │   │   ├── chrome-for-testing_118.md
│   │   │   ├── chrome-for-testing_119.md
│   │   │   ├── chrome-for-testing_120.md
│   │   │   ├── chrome-for-testing_121.md
│   │   │   ├── chrome-for-testing_122.md
│   │   │   ├── chrome-for-testing_123.md
│   │   │   ├── chrome-for-testing_124.md
│   │   │   ├── chrome-for-testing_125.md
│   │   │   ├── chrome-for-testing_126.md
│   │   │   ├── chrome-for-testing_127.md
│   │   │   ├── chrome-for-testing_128.md
│   │   │   ├── chrome-for-testing_129.md
│   │   │   ├── chrome-for-testing_130.md
│   │   │   ├── chrome-for-testing_131.md
│   │   │   ├── chrome-for-testing_132.md
│   │   │   ├── chrome-for-testing_133.md
│   │   │   ├── chrome-for-testing_134.md
│   │   │   ├── chrome-for-testing_135.md
│   │   │   ├── chrome-for-testing_136.md
│   │   │   ├── chrome-for-testing_137.md
│   │   │   ├── chrome-for-testing_138.md
│   │   │   ├── chrome-for-testing_139.md
│   │   │   ├── chrome-for-testing_140.md
│   │   │   ├── chrome-for-testing_141.md
│   │   │   ├── chrome-for-testing_142.md
│   │   │   ├── chrome-for-testing_143.md
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_141.md
│   │   │   ├── chrome_142.md
│   │   │   ├── chrome_143.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── edge_141.md
│   │   │   ├── edge_142.md
│   │   │   ├── edge_143.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_143.md
│   │   │   ├── firefox_144.md
│   │   │   ├── firefox_145.md
│   │   │   ├── firefox_146.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   └── 4.40.0/
│   │       ├── chrome-for-testing_113.md
│   │       ├── chrome-for-testing_114.md
│   │       ├── chrome-for-testing_115.md
│   │       ├── chrome-for-testing_116.md
│   │       ├── chrome-for-testing_117.md
│   │       ├── chrome-for-testing_118.md
│   │       ├── chrome-for-testing_119.md
│   │       ├── chrome-for-testing_120.md
│   │       ├── chrome-for-testing_121.md
│   │       ├── chrome-for-testing_122.md
│   │       ├── chrome-for-testing_123.md
│   │       ├── chrome-for-testing_124.md
│   │       ├── chrome-for-testing_125.md
│   │       ├── chrome-for-testing_126.md
│   │       ├── chrome-for-testing_127.md
│   │       ├── chrome-for-testing_128.md
│   │       ├── chrome-for-testing_129.md
│   │       ├── chrome-for-testing_130.md
│   │       ├── chrome-for-testing_131.md
│   │       ├── chrome-for-testing_132.md
│   │       ├── chrome-for-testing_133.md
│   │       ├── chrome-for-testing_134.md
│   │       ├── chrome-for-testing_135.md
│   │       ├── chrome-for-testing_136.md
│   │       ├── chrome-for-testing_137.md
│   │       ├── chrome-for-testing_138.md
│   │       ├── chrome-for-testing_139.md
│   │       ├── chrome-for-testing_140.md
│   │       ├── chrome-for-testing_141.md
│   │       ├── chrome-for-testing_142.md
│   │       ├── chrome-for-testing_143.md
│   │       ├── chrome-for-testing_144.md
│   │       ├── chrome-for-testing_145.md
│   │       ├── chrome_100.md
│   │       ├── chrome_101.md
│   │       ├── chrome_102.md
│   │       ├── chrome_103.md
│   │       ├── chrome_104.md
│   │       ├── chrome_105.md
│   │       ├── chrome_106.md
│   │       ├── chrome_107.md
│   │       ├── chrome_108.md
│   │       ├── chrome_109.md
│   │       ├── chrome_110.md
│   │       ├── chrome_111.md
│   │       ├── chrome_112.md
│   │       ├── chrome_113.md
│   │       ├── chrome_114.md
│   │       ├── chrome_115.md
│   │       ├── chrome_116.md
│   │       ├── chrome_117.md
│   │       ├── chrome_118.md
│   │       ├── chrome_119.md
│   │       ├── chrome_120.md
│   │       ├── chrome_121.md
│   │       ├── chrome_122.md
│   │       ├── chrome_123.md
│   │       ├── chrome_124.md
│   │       ├── chrome_125.md
│   │       ├── chrome_126.md
│   │       ├── chrome_127.md
│   │       ├── chrome_128.md
│   │       ├── chrome_129.md
│   │       ├── chrome_130.md
│   │       ├── chrome_131.md
│   │       ├── chrome_132.md
│   │       ├── chrome_133.md
│   │       ├── chrome_134.md
│   │       ├── chrome_135.md
│   │       ├── chrome_136.md
│   │       ├── chrome_137.md
│   │       ├── chrome_138.md
│   │       ├── chrome_139.md
│   │       ├── chrome_140.md
│   │       ├── chrome_141.md
│   │       ├── chrome_142.md
│   │       ├── chrome_143.md
│   │       ├── chrome_144.md
│   │       ├── chrome_95.md
│   │       ├── chrome_96.md
│   │       ├── chrome_97.md
│   │       ├── chrome_98.md
│   │       ├── chrome_99.md
│   │       ├── edge_114.md
│   │       ├── edge_115.md
│   │       ├── edge_116.md
│   │       ├── edge_117.md
│   │       ├── edge_118.md
│   │       ├── edge_119.md
│   │       ├── edge_120.md
│   │       ├── edge_121.md
│   │       ├── edge_122.md
│   │       ├── edge_123.md
│   │       ├── edge_124.md
│   │       ├── edge_125.md
│   │       ├── edge_126.md
│   │       ├── edge_127.md
│   │       ├── edge_128.md
│   │       ├── edge_129.md
│   │       ├── edge_130.md
│   │       ├── edge_131.md
│   │       ├── edge_132.md
│   │       ├── edge_133.md
│   │       ├── edge_134.md
│   │       ├── edge_135.md
│   │       ├── edge_136.md
│   │       ├── edge_137.md
│   │       ├── edge_138.md
│   │       ├── edge_139.md
│   │       ├── edge_140.md
│   │       ├── edge_141.md
│   │       ├── edge_142.md
│   │       ├── edge_143.md
│   │       ├── edge_144.md
│   │       ├── firefox_100.md
│   │       ├── firefox_101.md
│   │       ├── firefox_102.md
│   │       ├── firefox_103.md
│   │       ├── firefox_104.md
│   │       ├── firefox_105.md
│   │       ├── firefox_106.md
│   │       ├── firefox_107.md
│   │       ├── firefox_108.md
│   │       ├── firefox_109.md
│   │       ├── firefox_110.md
│   │       ├── firefox_111.md
│   │       ├── firefox_112.md
│   │       ├── firefox_113.md
│   │       ├── firefox_114.md
│   │       ├── firefox_115.md
│   │       ├── firefox_116.md
│   │       ├── firefox_117.md
│   │       ├── firefox_118.md
│   │       ├── firefox_119.md
│   │       ├── firefox_120.md
│   │       ├── firefox_121.md
│   │       ├── firefox_122.md
│   │       ├── firefox_123.md
│   │       ├── firefox_124.md
│   │       ├── firefox_125.md
│   │       ├── firefox_126.md
│   │       ├── firefox_127.md
│   │       ├── firefox_128.md
│   │       ├── firefox_129.md
│   │       ├── firefox_130.md
│   │       ├── firefox_131.md
│   │       ├── firefox_132.md
│   │       ├── firefox_133.md
│   │       ├── firefox_134.md
│   │       ├── firefox_135.md
│   │       ├── firefox_136.md
│   │       ├── firefox_137.md
│   │       ├── firefox_138.md
│   │       ├── firefox_139.md
│   │       ├── firefox_140.md
│   │       ├── firefox_141.md
│   │       ├── firefox_142.md
│   │       ├── firefox_143.md
│   │       ├── firefox_144.md
│   │       ├── firefox_145.md
│   │       ├── firefox_146.md
│   │       ├── firefox_147.md
│   │       ├── firefox_98.md
│   │       └── firefox_99.md
│   └── generate-matrix-readme.py
├── CONTRIBUTING.md
├── Distributor/
│   ├── Dockerfile
│   ├── selenium-grid-distributor.conf
│   └── start-selenium-grid-distributor.sh
├── ENV_VARIABLES.md
├── EventBus/
│   ├── Dockerfile
│   ├── selenium-grid-eventbus.conf
│   └── start-selenium-grid-eventbus.sh
├── Hub/
│   ├── Dockerfile
│   ├── example-config.toml
│   ├── selenium-grid-hub.conf
│   └── start-selenium-grid-hub.sh
├── LICENSE.md
├── Makefile
├── NodeAllBrowsers/
│   ├── Dockerfile
│   ├── fluxbox-menu-browser-aarch64
│   └── fluxbox-menu-browser-amd64
├── NodeBase/
│   ├── Dockerfile
│   ├── fluxbox-menu
│   ├── generate_config
│   ├── generate_relay_config
│   ├── json_merge.py
│   ├── selenium.conf
│   ├── start-novnc.sh
│   ├── start-selenium-node.sh
│   ├── start-vnc.sh
│   └── start-xvfb.sh
├── NodeChrome/
│   ├── Dockerfile
│   ├── chrome-cleanup.conf
│   ├── chrome-cleanup.sh
│   ├── fluxbox-menu-browser
│   ├── install-chrome-for-testing.sh
│   ├── install-chrome.sh
│   ├── install-chromedriver.sh
│   ├── update-chrome-components.sh
│   └── wrap_chrome_binary
├── NodeChromium/
│   ├── Dockerfile
│   ├── chrome-cleanup.conf
│   ├── chrome-cleanup.sh
│   ├── fluxbox-menu-browser
│   └── wrap_chromium_binary
├── NodeDocker/
│   ├── Dockerfile
│   ├── config.toml
│   ├── selenium-grid-docker.conf
│   ├── start-selenium-grid-docker.sh
│   └── start-socat.sh
├── NodeEdge/
│   ├── Dockerfile
│   ├── edge-cleanup.conf
│   ├── edge-cleanup.sh
│   ├── fluxbox-menu-browser
│   └── wrap_edge_binary
├── NodeFirefox/
│   ├── Dockerfile
│   ├── firefox-cleanup.conf
│   ├── firefox-cleanup.sh
│   ├── fluxbox-menu-browser
│   ├── get_lang_package.sh
│   ├── install-firefox-apt.sh
│   └── install-firefox-package.sh
├── NodeKubernetes/
│   ├── Dockerfile
│   ├── config.toml
│   ├── selenium-grid-kubernetes.conf
│   └── start-selenium-grid-kubernetes.sh
├── README.md
├── Router/
│   ├── Dockerfile
│   ├── selenium-grid-router.conf
│   └── start-selenium-grid-router.sh
├── SessionQueue/
│   ├── Dockerfile
│   ├── selenium-grid-session-queue.conf
│   └── start-selenium-grid-session-queue.sh
├── Sessions/
│   ├── Dockerfile
│   ├── generate_config
│   ├── init.sql
│   ├── selenium-grid-sessions.conf
│   └── start-selenium-grid-sessions.sh
├── Standalone/
│   ├── Dockerfile
│   ├── selenium.conf
│   └── start-selenium-standalone.sh
├── StandaloneDocker/
│   ├── Dockerfile
│   └── start-selenium-grid-docker.sh
├── StandaloneKubernetes/
│   ├── Dockerfile
│   └── start-selenium-grid-kubernetes.sh
├── Video/
│   ├── Dockerfile
│   ├── entry_point.sh
│   ├── recorder.conf
│   ├── upload.sh
│   ├── uploader.conf
│   ├── validate_endpoint.py
│   ├── video.sh
│   ├── video_graphQLQuery.py
│   ├── video_gridUrl.py
│   ├── video_nodeQuery.py
│   ├── video_ready.py
│   ├── video_recorder.py
│   ├── video_service.py
│   └── video_uploader.py
├── charts/
│   └── selenium-grid/
│       ├── .helmignore
│       ├── CHANGELOG.md
│       ├── CONFIGURATION.md
│       ├── Chart.yaml
│       ├── MIGRATION_INGRESS_NGINX_TO_TRAEFIK.md
│       ├── README.md
│       ├── TESTING.md
│       ├── certs/
│       │   ├── add-cert-helper.sh
│       │   ├── add-jks-helper.sh
│       │   ├── gen-cert-helper.sh
│       │   ├── server.jks
│       │   ├── server.pass
│       │   ├── tls.crt
│       │   └── tls.key
│       ├── configs/
│       │   ├── distributor/
│       │   │   └── distributorProbe.sh
│       │   ├── node/
│       │   │   ├── nodeGridUrl.sh
│       │   │   ├── nodePreStop.sh
│       │   │   ├── nodeProbe.sh
│       │   │   └── nodeProbeReadiness.sh
│       │   ├── router/
│       │   │   ├── routerGraphQLUrl.sh
│       │   │   └── routerProbe.sh
│       │   ├── scrape/
│       │   │   └── selenium-grid.yaml
│       │   └── uploader/
│       │       └── s3/
│       │           └── upload.sh
│       ├── multiple-nodes-platform-relay.yaml
│       ├── multiple-nodes-platform-version.yaml
│       ├── multiple-nodes-platform.yaml
│       ├── templates/
│       │   ├── NOTES.txt
│       │   ├── _helpers.tpl
│       │   ├── _nameHelpers.tpl
│       │   ├── basic-auth-secret.yaml
│       │   ├── chrome-node-deployment.yaml
│       │   ├── chrome-node-hpa.yaml
│       │   ├── chrome-node-scaledjobs.yaml
│       │   ├── chrome-node-service.yaml
│       │   ├── distributor-configmap.yaml
│       │   ├── distributor-deployment.yaml
│       │   ├── distributor-service.yaml
│       │   ├── edge-node-deployment.yaml
│       │   ├── edge-node-hpa.yaml
│       │   ├── edge-node-scaledjob.yaml
│       │   ├── edge-node-service.yaml
│       │   ├── event-bus-configmap.yaml
│       │   ├── event-bus-deployment.yaml
│       │   ├── event-bus-service.yaml
│       │   ├── firefox-node-deployment.yaml
│       │   ├── firefox-node-hpa.yaml
│       │   ├── firefox-node-scaledjob.yaml
│       │   ├── firefox-node-service.yaml
│       │   ├── hub-deployment.yaml
│       │   ├── hub-service.yaml
│       │   ├── ingress.yaml
│       │   ├── jaeger-ingress.yaml
│       │   ├── logging-configmap.yaml
│       │   ├── monitoring-exporter-deployment.yaml
│       │   ├── monitoring-exporter-service.yaml
│       │   ├── monitoring-scape-secret.yaml
│       │   ├── networkpolicy.yaml
│       │   ├── node-configmap.yaml
│       │   ├── patch-keda/
│       │   │   ├── delete-keda-objects-job.yaml
│       │   │   ├── patch-keda-objects-cm.yaml
│       │   │   ├── patch-keda-objects-job.yaml
│       │   │   ├── rbac-role.yaml
│       │   │   └── rbac-rolebinding.yaml
│       │   ├── recorder-configmap.yaml
│       │   ├── relay-node-deployment.yaml
│       │   ├── relay-node-hpa.yaml
│       │   ├── relay-node-scaledjobs.yaml
│       │   ├── relay-node-service.yaml
│       │   ├── router-configmap.yaml
│       │   ├── router-deployment.yaml
│       │   ├── router-service.yaml
│       │   ├── secrets.yaml
│       │   ├── server-configmap.yaml
│       │   ├── serviceaccount.yaml
│       │   ├── session-map-configmap.yaml
│       │   ├── session-map-deployment.yaml
│       │   ├── session-map-service.yaml
│       │   ├── session-queue-configmap.yaml
│       │   ├── session-queue-deployment.yaml
│       │   ├── session-queue-service.yaml
│       │   ├── tls-cert-secret.yaml
│       │   ├── traefik-servers-transport.yaml
│       │   ├── trigger-auth.yaml
│       │   ├── uploader-configmap.yaml
│       │   └── video-manager/
│       │       ├── file-browser-deployment.yaml
│       │       ├── file-browser-ingress.yaml
│       │       └── file-browser-service.yaml
│       └── values.yaml
├── docker-compose-v2-tracing.yml
├── docker-compose-v2.yml
├── docker-compose-v3-basicauth.yml
├── docker-compose-v3-beta-channel.yml
├── docker-compose-v3-dev-channel.yml
├── docker-compose-v3-dev.yml
├── docker-compose-v3-dynamic-grid.yml
├── docker-compose-v3-full-grid-dev.yml
├── docker-compose-v3-full-grid-external-datastore.yml
├── docker-compose-v3-full-grid-nightly.yml
├── docker-compose-v3-full-grid-secure.yml
├── docker-compose-v3-full-grid-swarm.yml
├── docker-compose-v3-full-grid-tracing.yml
├── docker-compose-v3-full-grid.yml
├── docker-compose-v3-node-all-browsers.yml
├── docker-compose-v3-swarm.yml
├── docker-compose-v3-tracing.yml
├── docker-compose-v3-video-in-node.yml
├── docker-compose-v3-video-upload-dynamic-grid.yml
├── docker-compose-v3-video-upload-standalone.yml
├── docker-compose-v3-video-upload.yml
├── docker-compose-v3-video.yml
├── docker-compose-v3.yml
├── generate_chart_changelog.sh
├── generate_release_notes.sh
├── generate_sbom.sh
├── kubernetes/
│   ├── DynamicGrid/
│   │   ├── BaseConfig/
│   │   │   ├── configmap.yaml
│   │   │   ├── pvc.yaml
│   │   │   └── rbac.yaml
│   │   ├── Hub_Node/
│   │   │   ├── hub-deployment.yaml
│   │   │   ├── hub-svc.yaml
│   │   │   └── node-kubernetes-deployment.yaml
│   │   ├── README.md
│   │   └── Standalone/
│   │       └── standalone-kubernetes.yaml
│   ├── Hub_Node/
│   │   ├── hub-deployment.yaml
│   │   ├── hub-svc.yaml
│   │   ├── node-chrome-deployment.yaml
│   │   ├── node-edge-deployment.yaml
│   │   └── node-firefox-deployment.yaml
│   ├── README.md
│   └── Standalone/
│       ├── standalone-chrome.yaml
│       ├── standalone-edge.yaml
│       └── standalone-firefox.yaml
├── renovate.json
├── tag_and_push_browser_images.sh
├── tests/
│   ├── .dockerignore
│   ├── AutoscalingTests/
│   │   ├── __init__.py
│   │   ├── common.py
│   │   ├── test_scale_chaos.py
│   │   └── test_scale_up.py
│   ├── CDPTests/
│   │   ├── .gitignore
│   │   ├── bootstrap.sh
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   └── tests/
│   │       └── Tests.ts
│   ├── Dockerfile
│   ├── Dockerfile.emulator
│   ├── README.md
│   ├── SeleniumJavaTests/
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── bootstrap_java.sh
│   │   ├── build.gradle
│   │   ├── gradle/
│   │   │   └── wrapper/
│   │   │       ├── gradle-wrapper.jar
│   │   │       └── gradle-wrapper.properties
│   │   ├── gradlew
│   │   ├── gradlew.bat
│   │   ├── settings.gradle
│   │   └── src/
│   │       └── test/
│   │           └── java/
│   │               └── SeleniumTests.java
│   ├── SeleniumTests/
│   │   └── __init__.py
│   ├── SmokeTests/
│   │   └── __init__.py
│   ├── bootstrap.sh
│   ├── build-backward-compatible/
│   │   ├── add_selenium_version.py
│   │   ├── bootstrap.sh
│   │   ├── browser-matrix.yml
│   │   ├── builder.py
│   │   ├── fetch_chrome_for_testing_version.py
│   │   ├── fetch_firefox_version.py
│   │   ├── fetch_version.py
│   │   ├── firefox-matrix.yml
│   │   ├── selenium-matrix.yml
│   │   └── update_workflow_versions.py
│   ├── charts/
│   │   ├── bootstrap.sh
│   │   ├── ci/
│   │   │   ├── DeploymentAutoscaling-values.yaml
│   │   │   ├── JobAutoscaling-values.yaml
│   │   │   ├── NoAutoscaling-values.yaml
│   │   │   ├── base-auth-ingress-values.yaml
│   │   │   ├── base-recorder-values.yaml
│   │   │   ├── base-resources-values.yaml
│   │   │   ├── base-subPath-values.yaml
│   │   │   ├── base-tls-values.yaml
│   │   │   ├── local-pvc.yaml
│   │   │   ├── nameOverride-values.yaml
│   │   │   ├── the-internet-deployment.yaml
│   │   │   └── uploader.conf
│   │   ├── config/
│   │   │   ├── ct.yaml
│   │   │   └── kind-cluster.yaml
│   │   ├── make/
│   │   │   ├── chart_build.sh
│   │   │   ├── chart_check_env.sh
│   │   │   ├── chart_cluster_cleanup.sh
│   │   │   ├── chart_cluster_setup.sh
│   │   │   ├── chart_release.sh
│   │   │   ├── chart_setup_env.sh
│   │   │   └── chart_test.sh
│   │   ├── refValues/
│   │   │   ├── README.md
│   │   │   ├── aws-s3-upload-secret.yaml
│   │   │   ├── local-pvc-docker-desktop.yaml
│   │   │   ├── sample-aws.yaml
│   │   │   ├── simplex-docker-desktop.yaml
│   │   │   └── simplex-minikube.yaml
│   │   └── templates/
│   │       ├── render/
│   │       │   ├── dummy.yaml
│   │       │   ├── dummy_external.sh
│   │       │   └── dummy_solution.yaml
│   │       ├── test.py
│   │       └── test_scaled_job.py
│   ├── config.toml
│   ├── customCACert/
│   │   ├── Dockerfile
│   │   └── bootstrap.sh
│   ├── docker-compose-v3-dev-arm64.yml
│   ├── docker-compose-v3-event-driven-arm64.yml
│   ├── docker-compose-v3-event-driven-standalone-arm64.yml
│   ├── docker-compose-v3-get-started-arm64.yml
│   ├── docker-compose-v3-test-node-docker.yaml
│   ├── docker-compose-v3-test-node-relay.yml
│   ├── docker-compose-v3-test-parallel.yml
│   ├── docker-compose-v3-test-standalone-docker.yaml
│   ├── docker-compose-v3-test-standalone.yml
│   ├── docker-compose-v3-test-video.yml
│   ├── get_started.py
│   ├── relay_config.toml
│   ├── requirements.txt
│   ├── standalone_docker_config.toml
│   ├── test.py
│   └── test_grid_ui.py
└── update_tag_in_docs_and_files.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .circleci/config.bak
================================================
version: 2.1

workflows:
  build-and-test-multi-arch:
    jobs:
      - kubernetes-test:
          name: "K8s test - Autoscaling disabled"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.26.15'
          test-strategy: disabled
          cluster: 'minikube'
          helm-version: 'v3.11.3'
          docker-version: '24.0.9'
          test-upgrade: true
      - kubernetes-test:
          name: "K8s test - Autoscaling Jobs"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.27.16'
          test-strategy: job
          cluster: 'minikube'
          helm-version: 'v3.12.3'
          docker-version: '24.0.9'
          test-upgrade: true
      - kubernetes-test:
          name: "K8s test - Autoscaling Deployments"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.28.15'
          test-strategy: deployment
          cluster: 'minikube'
          helm-version: 'v3.13.3'
          docker-version: '24.0.9'
          test-upgrade: true
      - kubernetes-test:
          name: "K8s test - Autoscaling Jobs - HTTPS"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.29.13'
          test-strategy: job_https
          cluster: 'minikube'
          helm-version: 'v3.14.3'
          docker-version: '25.0.5'
          test-upgrade: true
      - kubernetes-test:
          name: "K8s test - Autoscaling Jobs - Ingress hostname"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.30.9'
          test-strategy: job_hostname
          cluster: 'minikube'
          helm-version: 'v3.15.4'
          docker-version: '26.1.4'
          test-upgrade: true
      - kubernetes-test:
          name: "K8s test - Autoscaling Deployments - HTTPS"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.31.5'
          test-strategy: deployment_https
          cluster: 'minikube'
          helm-version: 'v3.16.4'
          docker-version: '27.4.1'
          test-upgrade: true
      - kubernetes-test:
          name: "K8s test - Playwright Connect Autoscaling Grid"
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          k8s-version: 'v1.32.1'
          test-strategy: playwright_connect_grid
          cluster: 'minikube'
          helm-version: 'v3.17.0'
          docker-version: '26.1.4'
          test-upgrade: true
      - docker-test:
          name: "Docker test - Use random user (true)"
          test-strategy: test
          use-random-user: true
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: false
          enable-managed-downloads: false
      - docker-test:
          name: "Docker test - Use random user (false)"
          test-strategy: test
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: false
          enable-managed-downloads: false
      - docker-test:
          name: "Docker test - Video recording"
          test-strategy: test_video
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: true
          enable-managed-downloads: true
      - docker-test:
          name: "Docker test - Video recording dynamic file name"
          test-strategy: test_video_dynamic_name
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: true
          enable-managed-downloads: true
      - docker-test:
          name: "Docker test - Video recording standalone"
          test-strategy: test_video_standalone
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: true
          enable-managed-downloads: true
      - docker-test:
          name: "Docker test - Dynamic Grid"
          test-strategy: test_node_docker
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: true
          enable-managed-downloads: false
      - docker-test:
          name: "Docker test - Dynamic Grid Standalone"
          test-strategy: test_standalone_docker
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: true
          enable-managed-downloads: true
      - docker-test:
          name: "Docker test - Parallel execution"
          test-strategy: test_parallel
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64large
          firefox-install-lang-package: false
          enable-managed-downloads: true
      - docker-test:
          name: "Docker test - Node relay commands"
          test-strategy: test_node_relay
          use-random-user: false
          platforms: linux/arm64
          machine-type: ubuntu2204arm64
          firefox-install-lang-package: true
          enable-managed-downloads: true

executors:
  ubuntu2204arm64:
    machine:
      image: ubuntu-2204:current
    resource_class: arm.medium
  ubuntu2204arm64large:
    machine:
      image: ubuntu-2204:current
    resource_class: arm.large

jobs:
  docker-test:
    parameters:
      platforms:
        type: string
      machine-type:
        type: executor
      test-strategy:
        type: string
      use-random-user:
        type: boolean
      firefox-install-lang-package:
        type: boolean
      enable-managed-downloads:
        type: boolean
    executor: << parameters.machine-type >>
    environment:
      NAMESPACE: selenium
      BUILD_DATE: today
      PLATFORMS: << parameters.platforms >>
      TEST_STRATEGY: << parameters.test-strategy >>
      USE_RANDOM_USER: << parameters.use-random-user >>
      TEST_FIREFOX_INSTALL_LANG_PACKAGE: << parameters.firefox-install-lang-package >>
      SELENIUM_ENABLE_MANAGED_DOWNLOADS: << parameters.enable-managed-downloads >>
    steps:
      - run:
          name: "Prepare workflow environment variables"
          command: |
            echo 'export BRANCH="${CIRCLE_BRANCH//\//-}"' >> $BASH_ENV
            cat $BASH_ENV
            source $BASH_ENV
      - checkout
      - run:
          name: "Setup environment"
          command: |
            make setup_dev_env
      - run:
          name: "Print system info"
          command: |
            uname -a
            docker info
      - run:
          name: "Build Docker images"
          command: |
            N=3
            while [ $N -gt 0 ]; do
              PLATFORMS=${PLATFORMS} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build
              status=$?
              if [ $status -eq 0 ]; then
                echo "Build images passed"
                exit 0
              else
                echo "Build failed. Retrying..."
                N=$((N-1))
                sleep 10
              fi
            done
            exit $status
      - run:
          name: "Test Docker images"
          no_output_timeout: 60m
          command: |
            max_iteration=3
            iteration=1
            until USE_RANDOM_USER_ID=${USE_RANDOM_USER} PLATFORMS=${PLATFORMS} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} \
              TEST_FIREFOX_INSTALL_LANG_PACKAGE=${TEST_FIREFOX_INSTALL_LANG_PACKAGE} SELENIUM_ENABLE_MANAGED_DOWNLOADS=${SELENIUM_ENABLE_MANAGED_DOWNLOADS} \
              make ${TEST_STRATEGY} >& /dev/null; [[ $? -eq 0 ]];
            do
              echo "Result unsuccessful"
              if [[ $iteration -eq $max_iteration ]]
              then
                break
              fi
              sleep 1
              ((iteration++))  
            done
            if [[ $iteration -eq $max_iteration ]]
            then
              echo "All of the $max_iteration trials failed!!!"
            else
              echo "Result successful"
            fi

  kubernetes-test:
    parameters:
      platforms:
        type: string
      machine-type:
        type: executor
      k8s-version:
        type: string
      test-strategy:
        type: string
      cluster:
        type: string
      helm-version:
        type: string
      docker-version:
        type: string
      test-upgrade:
        type: boolean
    executor: << parameters.machine-type >>
    environment:
      NAMESPACE: selenium
      BUILD_DATE: today
      PLATFORMS: << parameters.platforms >>
      TEST_STRATEGY: << parameters.test-strategy >>
      CLUSTER: << parameters.cluster >>
      KUBERNETES_VERSION: << parameters.k8s-version >>
      HELM_VERSION: << parameters.helm-version >>
      DOCKER_VERSION: << parameters.docker-version >>
      TEST_UPGRADE_CHART: << parameters.test-upgrade >>
      TEST_MULTIPLE_VERSIONS: false
      TEST_MULTIPLE_PLATFORMS: false
    steps:
      - run:
          name: "Prepare workflow environment variables"
          command: |
            echo 'export BRANCH="${CIRCLE_BRANCH//\//-}"' >> $BASH_ENV
            cat $BASH_ENV
            source $BASH_ENV
      - run:
          name: "Print system info"
          command: |
            uname -a
            docker info
      - checkout
      - run:
          name: "Set up Kubernetes environment"
          command: |
            make setup_dev_env
            CLUSTER=${CLUSTER} KUBERNETES_VERSION=${KUBERNETES_VERSION} NAME=${NAMESPACE} VERSION=${BRANCH} \
            BUILD_DATE=${BUILD_DATE} make chart_cluster_setup
      - run:
          name: "Build Docker images"
          no_output_timeout: 30m
          command: |
            N=3
            while [ $N -gt 0 ]; do
              PLATFORMS=${PLATFORMS} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build
              status=$?
              if [ $status -eq 0 ]; then
                echo "Build images passed"
                exit 0
              else
                echo "Build failed. Retrying..."
                N=$((N-1))
                sleep 10
              fi
            done
            exit $status
      - run:
          name: "Build Helm charts"
          command: |
            BUILD_DATE=${BUILD_DATE} make chart_build
            echo "export CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $BASH_ENV
            echo "export CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $BASH_ENV
            source $BASH_ENV
      - run:
          name: "Test Selenium Grid on Kubernetes"
          no_output_timeout: 60m
          command: |
            max_iteration=3
            iteration=1
            until PLATFORMS=${PLATFORMS} NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} TEST_UPGRADE_CHART=false \
              TEST_MULTIPLE_VERSIONS=${TEST_MULTIPLE_VERSIONS} TEST_MULTIPLE_PLATFORMS=${TEST_MULTIPLE_PLATFORMS} \
              make chart_test_autoscaling_${TEST_STRATEGY} && make test_video_integrity >& /dev/null; [[ $? -eq 0 ]];
            do
              echo "Result unsuccessful"
              if [[ $iteration -eq $max_iteration ]]
              then
                break
              fi
              sleep 1
              ((iteration++))  
            done
            if [[ $iteration -eq $max_iteration ]]
            then
              echo "All of the $max_iteration trials failed!!!"
            else
              echo "Result successful"
            fi
      - run:
          name: "Clean-up Kubernetes environment"
          command: |
            CLUSTER=${CLUSTER} make chart_cluster_cleanup


================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

[{Dockerfile,*.json,*.sh}]
indent_style = space
indent_size = 2

[*.md]
indent_style = space
indent_size = 4

[Makefile]
indent_style = tab
indent_size = 2


================================================
FILE: .ffmpeg/Dockerfile
================================================
FROM ubuntu:noble AS builder
ARG FFMPEG_VERSION="8.0"
ARG RCLONE_VER="v1.73-stable"
ARG GO_VERSION="latest"
#ARG GO_CRYPTO_VERSION="v0.36.0"
#ARG GO_OAUTH2_VERSION="v0.27.0"
#ARG GO_NET_VERSION="v0.38.0"
#ARG GOLANG_JWT_V4_VERSION="v4.5.2"
#ARG GOLANG_JWT_V5_VERSION="v5.2.2"

USER root

#======================================
# Install build tools
#======================================
ARG TOOLS_DEPS="autoconf automake cmake libfreetype6 gcc build-essential libtool make nasm pkg-config zlib1g-dev numactl \
libnuma-dev yasm git curl jq wget ca-certificates \
libx11-dev libxcb1-dev libpulse-dev libasound2-dev"

RUN apt-get update -qqy \
    && apt-get upgrade -yq \
    && apt-get -qqy --no-install-recommends install ${TOOLS_DEPS} \
    && apt-get -qyy clean \
    && mkdir -p /usr/local/src

RUN if [ "${GO_VERSION}" = "latest" ]; then \
        GO_VERSION=$(curl -sk https://go.dev/dl/?mode=json | jq -r '.[0].version'); \
    fi \
    && curl -skLO https://go.dev/dl/${GO_VERSION}.linux-$(dpkg --print-architecture).tar.gz \
    && tar -xf ${GO_VERSION}.linux-$(dpkg --print-architecture).tar.gz -C /usr/local \
    && rm -rf ${GO_VERSION}.linux-$(dpkg --print-architecture).tar.gz* \
    && ln -sf /usr/local/go/bin/go /usr/bin/go \
    && go version

RUN cd /usr/local/src \
    && git clone -b ${RCLONE_VER} --single-branch --depth 1 https://github.com/rclone/rclone.git \
    && cd rclone \
    # Patch deps version in go.mod to fix CVEs
#    && sed -i "s|golang.org/x/crypto v.*|golang.org/x/crypto ${GO_CRYPTO_VERSION}|g" go.mod \
#    && sed -i "s|golang.org/x/oauth2 v.*|golang.org/x/oauth2 ${GO_OAUTH2_VERSION}|g" go.mod \
#    && sed -i "s|golang.org/x/net v.*|golang.org/x/net ${GO_NET_VERSION}|g" go.mod \
#    && sed -i "s|github.com/golang-jwt/jwt/v5 v.*|github.com/golang-jwt/jwt/v5 ${GOLANG_JWT_V5_VERSION}|g" go.mod \
#    && sed -i "s|github.com/golang-jwt/jwt/v4 v.*|github.com/golang-jwt/jwt/v4 ${GOLANG_JWT_V4_VERSION}|g" go.mod \
#    && go mod tidy \
    # Build rclone
    && make \
    && mv ~/go/bin/rclone /usr/local/bin/ \
    && rclone version

#======================================
# Install x264 from source
#======================================
RUN cd /usr/local/src \
    && git clone https://code.videolan.org/videolan/x264.git --filter=blob:none \
    && cd x264 \
    && ./configure --prefix="/usr/local" --enable-static \
    && make \
    && make install

#======================================
# Install FFmpeg from source
#======================================
RUN cd /usr/local/src \
    && git clone -b release/${FFMPEG_VERSION} --single-branch --depth 1 https://github.com/FFmpeg/FFmpeg.git \
    && cd FFmpeg \
    && rm -rf .git \
    && PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" FFMPEG_VERSION=${FFMPEG_VERSION} ./configure \
    --prefix="/usr/local" \
    --extra-cflags="-I/usr/local/include" \
    --extra-ldflags="-L/usr/local/lib" \
    --pkg-config-flags="--static" \
    --enable-gpl \
    --enable-nonfree \
    --enable-libx264 \
    --enable-libxcb \
    --enable-libpulse \
    --enable-alsa \
    --enable-static \
    && make \
    && make install

# Final stage
FROM ubuntu:noble

USER root

COPY --from=builder /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
COPY --from=builder /usr/local/bin/rclone /usr/local/bin/rclone

RUN apt-get -qqy update \
    && apt-get -qqy --no-install-recommends install \
    libx11-6 libx11-xcb1 libxcb1 libpulse0 libasound2t64 \
    && apt-get -qqy update \
    && apt-get -yq upgrade \
    && rm -rf /var/lib/apt/lists/* /var/cache/apt/*

RUN ldd /usr/local/bin/ffmpeg \
    && ffmpeg -version \
    && rclone --version

USER 101


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 🐛 Bug Report
description: File a bug report
title: "[🐛 Bug]: "
labels: [bug, needs-triaging]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: |
        Describe clearly and concisely the bug including instructions showing how to reproduce it.
      placeholder: |
        Please add as many details as possible to avoid assumptions from our side. How do you
        trigger this bug? What did you expect to happen? Please walk us through it step by step.
    validations:
      required: true
  - type: textarea
    id: reproduce-command
    attributes:
      label: Command used to start Selenium Grid with Docker (or Kubernetes)
      description: |
        What command do you use to start Selenium Grid with Docker (or Kubernetes)?
      placeholder: |
        Please share the script or docker compose file used. This will be automatically
        formatted into code, so no need for backticks.
        If Kubernetes used, please share the YAML file, or chart values used to deploy the cluster.
        Be sure to include an SSCCE (Short, Self Contained, Correct 
        [compilable] example) http://sscce.org/
      render: shell
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Relevant log output
      description: |
        Please copy and paste any relevant log output. This will be automatically formatted
        into code, so no need for backticks.
      render: shell
    validations:
      required: true
  - type: input
    id: operating-system
    attributes:
      label: Operating System
      description: What host operating system are you using to run docker-selenium?
      placeholder: Windows 10? macOS BigSur? Ubuntu? Kubernetes (Minikube, EKS, GKE, AKS, OpenShift, Rancher, etc.) version?
    validations:
      required: true
  - type: input
    id: version
    attributes:
      label: Docker Selenium version (image tag)
      description: What version of Docker Selenium are you using?
      placeholder: 4.41.0-20260222? Please use the full tag, avoid "latest"
    validations:
      required: true
  - type: input
    id: chart-version
    attributes:
      label: Selenium Grid chart version (chart version)
      description: What version of Selenium Grid chart are you using?
      placeholder: 0.36.3?
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: 💬 Selenium Community Support and Questions
    url: https://www.selenium.dev/support/
    about: Useful links to ask questions and join the different Selenium chat rooms.
  - name: 📖 Docker-Selenium Documentation
    url: https://github.com/SeleniumHQ/docker-selenium
    about: Please check the project README before filling out an issue.
  - name: 📖 Selenium Documentation
    url: https://www.selenium.dev/documentation/
    about: Issues while running tests? Please check the Selenium documentation before filling out an issue.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_proposal.yml
================================================
name: 🚀 Feature Proposal
description: Propose a feature
title: "[🚀 Feature]: "
labels: [feature, needs-triaging]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to propose a feature!
  - type: textarea
    id: feature-description
    attributes:
      label: Feature and motivation
      description: |
        Describe clearly and concisely the feature you are proposing, what is the motivation
        behind it.
      placeholder: |
        Help us to understand your proposal by adding as many details as possible, we will look into
        it and give you feedback as soon as possible.
    validations:
      required: true
  - type: textarea
    id: feature-example
    attributes:
      label: Usage example
      description: |
        How would you use this feature? 
      placeholder: |
        A clear example showing how this feature is useful for you and the Selenium community.
    validations:
      required: true


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!-- Thanks for sending us a PR to improve this project! If you are adding a 
feature or fixing a bug, and this needs more documentation, please add it to your PR. -->

**Thanks for contributing to the Docker-Selenium project!**
**A PR well described will help maintainers to quickly review and merge it**

Before submitting your PR, please check our [contributing](https://selenium.dev/documentation/en/contributing/) guidelines, applied for this repository.
Avoid large PRs, help reviewers by making them as simple and short as possible.


<!--- Provide a general summary of your changes in the Title above -->

### Description
<!--- Describe your changes in detail -->

### Motivation and Context
<!--- Why is this change required? What problem does it solve? -->

### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

### Checklist
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] I have read the [contributing](https://selenium.dev/documentation/en/contributing/) document.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
<!--- Provide a general summary of your changes in the Title above -->


================================================
FILE: .github/actions/get-latest-upstream/action.yml
================================================
name: Get Latest Upstream
description: Get the latest upstream release of Selenium
inputs:
  release:
    description: 'Test a new release process'
    required: false
    type: boolean
    default: false
  gh_cli_token:
    description: 'GitHub CLI authentication token'
    required: true
    type: secret

runs:
  using: "composite"
  steps:
    - name: Get latest upstream
      shell: bash
      env:
        AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
      run: |
        sudo apt update
        sudo apt install jq
        AUTH_HEADER="Authorization: token ${{ inputs.gh_cli_token }}"
        if [ "${{ inputs.release }}" = "true" ]; then
          echo "Getting the latest stable release."
          RELEASE=$(curl -s -H "$AUTH_HEADER" https://api.github.com/repos/${AUTHORS}/selenium/releases | jq -r '[.[]? | select(.prerelease == false)] | .[0].tag_name')
        else
          echo "Getting the latest Nightly release."
          RELEASE=$(curl -s -H "$AUTH_HEADER" https://api.github.com/repos/${AUTHORS}/selenium/releases | jq -r '[.[]? | select(.prerelease == true)] | .[0].tag_name' || echo "")
          if [ -z "${RELEASE}" ] || [ "${RELEASE}" = "null" ]; then
            echo "Nightly release not found, getting the latest stable release."
            RELEASE=$(curl -s -H "$AUTH_HEADER" https://api.github.com/repos/${AUTHORS}/selenium/releases | jq -r '[.[]? | select(.prerelease == false)] | .[0].tag_name')
          fi
        fi
        jar_file=$(curl -s -H "$AUTH_HEADER" https://api.github.com/repos/${AUTHORS}/selenium/releases/tags/${RELEASE} | jq -r '.assets[] | select(.name | endswith(".jar")) | .name' | tail -n 1)
        echo "Server package: ${jar_file}"
        VERSION=$(echo $jar_file | sed 's/selenium-server-//;s/\.jar//')
        echo "BASE_RELEASE=${RELEASE} | BASE_VERSION=${VERSION} | VERSION=${VERSION}"
        echo "BASE_RELEASE=${RELEASE}" >> $GITHUB_ENV
        echo "BASE_VERSION=${VERSION}" >> $GITHUB_ENV
        echo "VERSION=${VERSION}" >> $GITHUB_ENV


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: docker
  directory: "/StandaloneChrome"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: docker
  directory: "/StandaloneFirefox"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: docker
  directory: "/NodeFirefox"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: docker
  directory: "/Hub"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: docker
  directory: "/NodeBase"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: docker
  directory: "/NodeChrome"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: docker
  directory: "/"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99
- package-ecosystem: github-actions
  directory: "/"
  schedule:
    interval: daily
    time: '08:00'
  open-pull-requests-limit: 99


================================================
FILE: .github/label-commenter-config.yml
================================================
# Configuration for Label Commenter - https://github.com/peaceiris/actions-label-commenter
labels:
  - name: needs-triaging
    labeled:
      issue:
        body: |
          @{{ issue.user.login }}, thank you for creating this issue. We will troubleshoot it as soon as we can.

          ---
          <details>
            <summary>Info for maintainers</summary>
            <div>
              <br>
              <p>
                Triage this issue by using labels.
              </p>
              <p>
                If information is missing, add a helpful comment and then <code>I-issue-template</code> label.
              </p>
              <p>
                If the issue is a question, add the <code>I-question</code> label.
              </p>
              <p>
                If the issue is valid but there is no time to troubleshoot it, consider adding the <code>help wanted</code> label.
              </p>
              <p>
                If the issue requires changes or fixes from an external project (e.g., ChromeDriver, GeckoDriver, MSEdgeDriver, W3C),
               add the applicable <code>G-*</code> label, and it will provide the correct link and auto-close the
               issue.
              </p>
              <p>
                After troubleshooting the issue, please add the <code>R-awaiting answer</code> label.
              </p>
              <p>
                Thank you!
              </p>
            </div>
          </details>
  - name: G-w3c
    labeled:
      issue:
        body: |
          Hi, @{{ issue.user.login }}.
          This issue has been determined to require a change to the
          [WebDriver W3C Specification](https://w3c.github.io/webdriver/) for Selenium to be able to support it.

          Please [create an issue](https://github.com/w3c/webdriver/issues/new) with the WebDriver project.

          Feel free to comment the issues that you raise back in this issue. Thank you.
        action: close
  - name: G-chromedriver
    labeled:
      issue:
        body: |
          Hi, @{{ issue.user.login }}.
          This issue has been determined to require fixes in [ChromeDriver](https://chromedriver.chromium.org/home).

          You can see if the feature is passing in the [Web Platform Tests](https://wpt.fyi/results/webdriver/tests).

          If it is something new, please [create an issue](https://bugs.chromium.org/p/chromedriver/issues/list) with the ChromeDriver team.
          Feel free to comment the issues that you raise back in this issue. Thank you.

        action: close
  - name: G-geckodriver
    labeled:
      issue:
        body: |
          Hi, @{{ issue.user.login }}.
          This issue has been determined to require fixes in [GeckoDriver](https://firefox-source-docs.mozilla.org/testing/geckodriver/).

          You can see if the feature is passing in the [Web Platform Tests](https://wpt.fyi/results/webdriver/tests).

          If it is something new, please [create an Issue](https://github.com/mozilla/geckodriver/issues/new) with the GeckoDriver team.

          Feel free to comment the issues that you raise back in this issue. Thank you.
        action: close
  - name: G-msedgedriver
    labeled:
      issue:
        body: |
          Hi, @{{ issue.user.login }}.
          This issue has been determined to require fixes in [MSEdgeDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)
          or in the IE Mode in Edge.

          You can see if the feature is passing in the [Web Platform Tests](https://wpt.fyi/results/webdriver/tests).

          If it is something new, please [create an Issue](https://github.com/MicrosoftEdge/EdgeWebDriver/issues/new) with the MSEdgeDriver team.

          Feel free to comment the issues that you raise back in this issue. Thank you.
        action: close
  - name: I-issue-template
    labeled:
      issue:
        body: |
          Hi, @{{ issue.user.login }}.
          Please follow the issue template, we need more information to reproduce the issue.

          Either a complete code snippet and URL/HTML (if more than one file is needed, provide a GitHub repo and instructions to run the code), the specific versions used, or a more detailed description to help us understand the issue.

          Note: If you cannot share your code and URL/HTML, any complete code snippet and URL/HTML that reproduces the issue is good enough.

          Reply to this issue when all information is provided, thank you.
  - name: I-question
    labeled:
      issue:
        body: |
          💬 Please ask questions at:
          * 📫 The [Selenium user group](https://groups.google.com/forum/#!forum/selenium-users)
          * 📮 [StackOverflow](https://stackoverflow.com/questions/tagged/selenium)
          * 🗣 Our [IRC/Slack/Matrix channels](https://www.selenium.dev/support/) where the community can help you as well
        action: close
  - name: help wanted
    labeled:
      issue:
        body: |
          This issue is looking for contributors.

          Please comment below or reach out to us through our [IRC/Slack/Matrix channels](https://www.selenium.dev/support/) if you are interested.


================================================
FILE: .github/workflows/build-ffmpeg.yml
================================================
name: Build and Deploy FFmpeg

on:
  push:
    branches:
      - trunk
    paths:
      - '.ffmpeg/Dockerfile'
  pull_request:
    paths:
      - '.ffmpeg/Dockerfile'
  workflow_dispatch:
    inputs:
      release:
        description: 'Deploy a new release'
        required: false
        type: boolean
        default: false

jobs:
  deploy:
    name: Build and Deploy FFmpeg
    runs-on: ubuntu-24.04
    permissions: write-all
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Sets build date
        run: |
          echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          make set_build_multiarch
          cat .env | xargs -I {} echo {} >> $GITHUB_ENV
        env:
          NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
          AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
      - name: Build images
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 300
          max_attempts: 2
          retry_wait_seconds: 60
          command: |
            PLATFORMS="${PLATFORMS}" make ffmpeg
            make tag_ffmpeg_latest
      - name: Login Docker Hub
        if: ${{ github.event.inputs.release == 'true' }}
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Deploy new images
        if: ${{ github.event.inputs.release == 'true' }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 5
          retry_wait_seconds: 300
          command: |
            make release_ffmpeg_latest


================================================
FILE: .github/workflows/build-test.yml
================================================
name: Build & test

on:
  workflow_call:
    secrets:
      DOCKER_USERNAME:
        required: false
      DOCKER_PASSWORD:
        required: false
    inputs:
      release:
        description: 'Test a new release process'
        required: false
        type: string
        default: 'false'
      test-patched-keda:
        description: 'Test patched KEDA (true/false)'
        required: false
        default: ''
        type: string
  workflow_dispatch:
    inputs:
      rerunFailedOnly:
        description: 'Rerun only failed jobs'
        required: false
        type: boolean
        default: true
      test-patched-keda:
        description: 'Test patched KEDA (true/false)'
        required: false
        default: ''
        type: string
  push:
    branches:
      - trunk
    paths-ignore:
      - '**.md'
      - '**/*.md'
      - 'CHANGELOG/**'
      - '.*'
  pull_request:
    paths-ignore:
      - '**.md'
      - '**/*.md'
      - 'CHANGELOG/**'
      - 'tests/build-backward-compatible/**'
      - 'scripts/generate_list_env_vars/**'

concurrency:
  group: ${{ github.workflow }}-${{ github.ref == github.ref_protected && github.run_id || github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions: write-all

env:
  GH_CLI_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  GH_CLI_TOKEN_PR: ${{ secrets.TRIGGER_CI_TOKEN || secrets.GITHUB_TOKEN }}
  RUN_ID: ${{ github.run_id }}
  RERUN_FAILED_ONLY: ${{ github.event.inputs.rerunFailedOnly || true }}
  RUN_ATTEMPT: ${{ github.run_attempt }}

jobs:
  lint-format:
    name: Lint scripts format
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
      - name: Lint format
        run: make lint_format_scripts

  docker-test:
    needs: [lint-format]
    if: contains(toJson(github.event.commits), '[skip test]') == false
    name: Test Selenium Grid on Docker
    uses: ./.github/workflows/docker-test.yml
    with:
      release: ${{ inputs.release == 'true' }}

  helm-chart-test:
    needs: [lint-format]
    if: contains(toJson(github.event.commits), '[skip test]') == false
    name: Test Selenium Grid on Kubernetes
    uses: ./.github/workflows/helm-chart-test.yml
    secrets: inherit
    with:
      release: ${{ inputs.release == 'true' }}
      test-patched-keda: ${{ github.event.inputs.test-patched-keda }}

  rerun-workflow-when-failure:
    name: Rerun workflow when failure
    needs:
      - docker-test
      - helm-chart-test
    if: failure() && ( github.run_attempt < 3 )
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
      - name: Install GitHub CLI
        run: |
          sudo apt update
          sudo apt install gh
      - name: Authenticate GitHub CLI for PR
        if: github.event_name == 'pull_request'
        run: |
          echo "$GH_CLI_TOKEN_PR" | gh auth login --with-token
      - name: Authenticate GitHub CLI
        if: github.event_name != 'pull_request'
        run: |
          echo "$GH_CLI_TOKEN" | gh auth login --with-token
      - name: Rerun workflow when failure
        run: |
          echo "Rerun workflow ID $RUN_ID in attempt #$(($RUN_ATTEMPT + 1))"
          gh workflow run rerun-failed.yml \
            --repo $GITHUB_REPOSITORY \
            --raw-field runId=$RUN_ID \
            --raw-field rerunFailedOnly=$RERUN_FAILED_ONLY


================================================
FILE: .github/workflows/create-changelog-pr.yml
================================================
name: Create changelog PR

on:
  workflow_call:
    inputs:
      grid-version:
        required: true
        type: string
      browser-name:
        required: true
        type: string
      browser-versions:
        required: true
        type: string
      run-id:
        required: true
        type: string

jobs:
  pr-results:
    name: Create a PR with changelog
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Check existing PR
        id: check-pr
        run: |
          PR_NUMBER=$(gh pr list --base trunk --head browser-node-changelog --json number --jq '.[0].number')
          if [ "$PR_NUMBER" != "null" ] && [ -n "$PR_NUMBER" ]; then
            echo "pr-exists=true" >> $GITHUB_OUTPUT
          else
            echo "pr-exists=false" >> $GITHUB_OUTPUT
          fi
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Checkout PR branch
        if: steps.check-pr.outputs.pr-exists == 'true'
        run: |
          git checkout browser-node-changelog
      - name: Create CHANGELOG directory
        run: mkdir -p ./CHANGELOG/${{ inputs.grid-version }}
      - name: Download results
        uses: actions/download-artifact@v8
        with:
          path: ./CHANGELOG/${{ inputs.grid-version }}
          pattern: 'image_tags_*'
          merge-multiple: 'true'
          run-id: ${{ inputs.run-id }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
      - name: Fetch latest version
        run: make update_browser_versions_matrix
      - name: Commit & Push changes
        if: steps.check-pr.outputs.pr-exists == 'true'
        uses: actions-js/push@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          author_email: "selenium-ci@users.noreply.github.com"
          author_name: "Selenium CI Bot"
          message: "[ci] Upload CHANGELOG for Node/Standalone ${{ inputs.browser-name }} version with Grid ${{ inputs.grid-version }}"
          empty: true
          rebase: true
          branch: "browser-node-changelog"
      - name: Create Pull Request
        if: steps.check-pr.outputs.pr-exists == 'false'
        uses: peter-evans/create-pull-request@main
        with:
          token: ${{ secrets.SELENIUM_CI_TOKEN }}
          commit-message: |
            [ci] Upload CHANGELOG for Node/Standalone ${{ inputs.browser-name }} version with Grid ${{ inputs.grid-version }}
            
            Browser versions: ${{ inputs.browser-versions }}
          title: "[ci] CHANGELOG for Node/Standalone browser versions with Grid ${{ inputs.grid-version }}"
          body: "This PR contains the CHANGELOG for Node/Standalone with backward browser versions"
          committer: 'Selenium CI Bot <selenium-ci@users.noreply.github.com>'
          author: 'Selenium CI Bot <selenium-ci@users.noreply.github.com>'
          branch: browser-node-changelog

================================================
FILE: .github/workflows/deploy.yml
================================================
name: Deploys

on:
  workflow_dispatch:
    inputs:
      stable:
        description: 'Use upstream stable build'
        required: true
        type: string
        default: 'true'
      release:
        description: 'Deploy a new release'
        required: false
        type: string
        default: 'true'
      skip-test:
        description: 'Skip the tests'
        required: false
        type: boolean
        default: false
      skip-commit:
        description: 'Skip the commit'
        required: false
        type: boolean
        default: false
      skip-build-push-image:
        description: 'Skip the build & push images'
        required: false
        type: boolean
        default: false
      build-date:
        description: 'Build date'
        required: false
        type: string
        default: ''
  push:
    branches:
      - trunk

jobs:
  build-test:
    name: Build and Test
    if: contains(toJson(github.event.commits), '[deploy]') == true || (github.event_name == 'workflow_dispatch' && github.event.inputs.skip-test == 'false')
    uses: ./.github/workflows/build-test.yml
    secrets: inherit
    with:
      release: ${{ github.event.inputs.stable || true }}

  deploy:
    needs:
      - build-test
    if: (contains(toJson(github.event.commits), '[deploy]') == true || (github.event_name == 'workflow_dispatch' && github.event.inputs.release == 'true')) && !failure() && !cancelled()
    name: Deploy and Release
    runs-on: ubuntu-24.04
    permissions: write-all
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: true
          android: true
          dotnet: true
          haskell: true
          large-packages: true
          docker-images: true
          swap-storage: true
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ github.event.inputs.stable || true }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          if [ -z "${BUILD_DATE}" ]; then
            echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          else
            echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
          fi
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          make set_build_multiarch
          cat .env | xargs -I {} echo {} >> $GITHUB_ENV
        env:
          NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
          AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
          BUILD_DATE: ${{ github.event.inputs.build-date || '' }}
      - name: Sets prerelease to false by default
        run: echo "PRERELEASE=false" >> $GITHUB_ENV
      - name: Get Grid version
        run: |
          echo ${BASE_VERSION}
          echo "GRID_VERSION=${BASE_VERSION}" >> $GITHUB_ENV
      - name: Is it a prerelease?
        run: echo "GRID_VERSION=${GRID_VERSION}-prerelease" >> $GITHUB_ENV && echo "PRERELEASE=true" >> $GITHUB_ENV
        if: contains(toJson(github.event.commits), '[prerelease]') == true
      - name: Display Grid version
        run: echo ${GRID_VERSION}
      - name: Sets env var for the next tag
        run: echo "NEXT_TAG=${GRID_VERSION}-${BUILD_DATE}" >> $GITHUB_ENV
      - name: Get latest tag
        run: echo "LATEST_TAG=$(git tag --sort=-version:refname | grep "^[^selenium]" | head -n 1)" >> $GITHUB_ENV
      - name: Display latest tag
        run: echo ${LATEST_TAG}
      - name: Update tag in docs and files
        run: ./update_tag_in_docs_and_files.sh ${LATEST_TAG} ${NEXT_TAG}
      - name: Update chart configuration docs
        run: make generate_readme_charts
      - name: Build Helm chart
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 5
          max_attempts: 3
          retry_wait_seconds: 10
          command: |
            SET_VERSION=false make chart_build
            echo "CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $GITHUB_ENV
            echo "CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $GITHUB_ENV
      - name: Render chart templates
        run: |
          make chart_render_template
          echo "PUBLISH_YAML_MANIFESTS=$(find ./tests/tests -name "k8s_*.yaml" | tr '\n' ',')" >> $GITHUB_ENV
      - name: Build images
        if: github.event.inputs.skip-build-push-image != 'true'
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 180
          max_attempts: 3
          retry_wait_seconds: 60
          command: PLATFORMS="${PLATFORMS}" VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make build
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Deploy new images
        if: github.event.inputs.skip-build-push-image != 'true'
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 5
          retry_wait_seconds: 300
          command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make release
      - name: Tag images as latest
        if: github.event.inputs.skip-build-push-image != 'true'
        run: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make tag_latest
      - name: Deploy latest tag
        if: github.event.inputs.skip-build-push-image != 'true'
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 5
          retry_wait_seconds: 300
          command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make release_latest
      - name: Update package versions
        run: make update_browser_versions_matrix
#          make generate_latest_sbom
#          make fetch_grid_scaler_resources
      - name: Tag browser images
        if: github.event.inputs.skip-build-push-image != 'true'
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 5
          retry_wait_seconds: 300
          command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} PUSH_IMAGE=true make tag_and_push_browser_images
      - name: Delete previous nightly tag & release if any
        uses: dev-drprasad/delete-tag-and-release@master
        with:
          tag_name: nightly
          github_token: ${{ secrets.GITHUB_TOKEN }}
          delete_release: true
      - name: Commit files
        if: env.LATEST_TAG != env.NEXT_TAG && github.event.inputs.skip-commit != 'true'
        run: |
          git config --local user.email "selenium-ci@users.noreply.github.com"
          git config --local user.name "Selenium CI Bot"
          git commit -m "[ci] Update tag ${RELEASE_TAG} in docs and files" -m "[skip test]" -a
          git pull --rebase
        env:
          RELEASE_TAG: "${{ env.GRID_VERSION }}-${{ env.BUILD_DATE }}"
      - name: Push changes
        if: env.LATEST_TAG != env.NEXT_TAG && github.event.inputs.skip-commit != 'true'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.SELENIUM_CI_TOKEN }}
          branch: trunk
      - name: Create release notes (release_notes.md)
        run: ./generate_release_notes.sh ${LATEST_TAG} origin/trunk ${GRID_VERSION} ${BUILD_DATE}
      - name: Upload release notes
        uses: actions/upload-artifact@main
        with:
          name: release_notes
          path: ./release_notes.md
          if-no-files-found: ignore
      - name: Create Release
        if: env.LATEST_TAG != env.NEXT_TAG
        id: create_release
        uses: softprops/action-gh-release@v2.6.1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          tag_name: "${{ env.GRID_VERSION }}-${{ env.BUILD_DATE }}"
          name: "${{ env.GRID_VERSION }}-${{ env.BUILD_DATE }}"
          body_path: "release_notes.md"
          generate_release_notes: true
          prerelease: ${{ env.PRERELEASE }}
          draft: false
          append_body: false
          discussion_category_name: "Announcements"
          files: |
            package_versions.txt
            ${{ env.PUBLISH_YAML_MANIFESTS }}


================================================
FILE: .github/workflows/docker-test.yml
================================================
name: Test Docker Selenium

on:
  workflow_call:
    inputs:
      release:
        description: 'Test a new release process'
        required: false
        type: string
        default: 'false'
  workflow_dispatch:
    inputs:
      request-timeout:
        description: 'Test parameter for different request timeout'
        required: false
        default: '400'
      parallel-hardening:
        description: 'Test parameter to enable hardening parallel tests'
        required: false
        default: 'true'
      log-level:
        description: 'Test parameter for different log level'
        required: false
        default: 'INFO'

permissions:
  contents: read

jobs:
  build-and-test:
    name: Test Docker Selenium
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - test-strategy: test
            use-random-user: true
            test-video: false
            build-all: true
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test
            use-random-user: false
            test-video: false
            build-all: true
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_video
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_video_dynamic_name
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_video_standalone
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_node_docker
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_standalone_docker
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_parallel
            use-random-user: false
            test-video: false
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          - test-strategy: test_node_relay
            use-random-user: false
            test-video: false
            build-all: false
            os: ubuntu-24.04
            firefox-install-lang-package:
            enable-managed-downloads:
          ###
          - test-strategy: test
            use-random-user: true
            test-video: false
            build-all: true
            os: ubuntu-24.04-arm
            firefox-install-lang-package: false
            enable-managed-downloads: false
          - test-strategy: test
            use-random-user: false
            test-video: false
            build-all: true
            os: ubuntu-24.04-arm
            firefox-install-lang-package: false
            enable-managed-downloads: false
          - test-strategy: test_video
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: true
            enable-managed-downloads: true
          - test-strategy: test_video_dynamic_name
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: true
            enable-managed-downloads: true
          - test-strategy: test_video_standalone
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: true
            enable-managed-downloads: true
          - test-strategy: test_node_docker
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: true
            enable-managed-downloads: false
          - test-strategy: test_standalone_docker
            use-random-user: false
            test-video: true
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: true
            enable-managed-downloads: true
          - test-strategy: test_parallel
            use-random-user: false
            test-video: false
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: false
            enable-managed-downloads: true
          - test-strategy: test_node_relay
            use-random-user: false
            test-video: false
            build-all: false
            os: ubuntu-24.04-arm
            firefox-install-lang-package: true
            enable-managed-downloads: true
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          large-packages: false
      - name: Checkout code
        uses: actions/checkout@main
      - name: Format and lint scripts
        run: |
          make format_shell_scripts
      - name: Disable QEMU
        if: matrix.test-strategy == 'test_node_relay' && contains(matrix.os, 'amd') == true
        run: echo "DOCKER_ENABLE_QEMU=false >> $GITHUB_ENV"
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            DOCKER_ENABLE_QEMU=${DOCKER_ENABLE_QEMU} make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set up Python
        uses: actions/setup-python@main
        with:
          python-version: '3.14'
          check-latest: true
      - name: Enable KVM
        if: matrix.test-strategy == 'test_node_relay' && contains(matrix.os, 'arm') == false
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm
      - name: Get branch name (only for push to branch)
        if: github.event_name == 'push'
        run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV
        env:
          PUSH_BRANCH: ${{ github.ref }}
      - name: Get target branch name (only for PRs)
        if: github.event_name == 'pull_request'
        run: echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV
        env:
          TARGET_BRANCH: ${{ github.head_ref }}
      - name: Output branch name
        run: echo ${BRANCH}
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ inputs.release || false }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          echo "AUTHORS=${AUTHORS}" >> $GITHUB_ENV
        env:
          AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
      - name: Build Docker images
        uses: nick-invision/retry@master
        if: matrix.build-all == true
        with:
          timeout_minutes: 90
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build
      - name: Set test parameters
        if: (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
        run: |
          echo "LOG_LEVEL=${LOG_LEVEL}" >> $GITHUB_ENV
          echo "TEST_PARALLEL_HARDENING=${TEST_PARALLEL_HARDENING}" >> $GITHUB_ENV
          echo "REQUEST_TIMEOUT=${REQUEST_TIMEOUT}" >> $GITHUB_ENV
        env:
          LOG_LEVEL: ${{ github.event.inputs.log-level || 'INFO' }}
          TEST_PARALLEL_HARDENING: ${{ github.event.inputs.parallel-hardening || 'true' }}
          REQUEST_TIMEOUT: ${{ github.event.inputs.request-timeout || '400' }}
      - name: Set environment variables
        run: |
          if [ -n "${TEST_FIREFOX_INSTALL_LANG_PACKAGE}" ]; then
            echo "TEST_FIREFOX_INSTALL_LANG_PACKAGE=${TEST_FIREFOX_INSTALL_LANG_PACKAGE}" >> $GITHUB_ENV
          fi
          if [ -n "${SELENIUM_ENABLE_MANAGED_DOWNLOADS}" ]; then
              echo "SELENIUM_ENABLE_MANAGED_DOWNLOADS=${SELENIUM_ENABLE_MANAGED_DOWNLOADS}" >> $GITHUB_ENV
          fi
        env:
          TEST_FIREFOX_INSTALL_LANG_PACKAGE: ${{ matrix.firefox-install-lang-package }}
          SELENIUM_ENABLE_MANAGED_DOWNLOADS: ${{ matrix.enable-managed-downloads }}
      - name: Run Docker Compose to ${{ matrix.test-strategy }} on AMD64
        if: contains(matrix.os, 'arm') == false
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 90
          max_attempts: 2
          retry_wait_seconds: 60
          command: |
            USE_RANDOM_USER_ID=${{ matrix.use-random-user }} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make ${{ matrix.test-strategy }}
      - name: Run Docker Compose to ${{ matrix.test-strategy }} on ARM64
        if: contains(matrix.os, 'arm') == true
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 90
          max_attempts: 2
          retry_wait_seconds: 60
          command: |
            USE_RANDOM_USER_ID=${{ matrix.use-random-user }} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} \
            TEST_FIREFOX_INSTALL_LANG_PACKAGE=${TEST_FIREFOX_INSTALL_LANG_PACKAGE} SELENIUM_ENABLE_MANAGED_DOWNLOADS=${SELENIUM_ENABLE_MANAGED_DOWNLOADS} \
            make ${{ matrix.test-strategy }}
      - name: Upload recorded video
        if: matrix.test-video == true
        uses: actions/upload-artifact@main
        with:
          name: "${{ matrix.test-strategy }}_artifacts_${{ matrix.os }}"
          path: ./tests/videos/
      - name: Clean up Docker
        if: always()
        run: docker system prune -af


================================================
FILE: .github/workflows/helm-chart-release.yml
================================================
name: Release Charts
concurrency:
  group: ${{ github.workflow }}

on:
  push:
    branches:
      - trunk
    paths:
      - 'charts/selenium-grid/Chart.yaml'
  workflow_dispatch:
    inputs:
      release:
        description: 'Deploy a new release'
        required: false
        type: string
        default: 'false'
      skip-test:
        description: 'Skip the tests'
        required: false
        type: boolean
        default: false
      skip-commit:
        description: 'Skip the commit'
        required: false
        type: boolean
        default: false

permissions: write-all

env:
  GH_CLI_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  GH_CLI_TOKEN_PR: ${{ secrets.TRIGGER_CI_TOKEN || secrets.GITHUB_TOKEN }}
  RUN_ID: ${{ github.run_id }}
  RERUN_FAILED_ONLY: ${{ github.event.inputs.rerunFailedOnly || true }}
  RUN_ATTEMPT: ${{ github.run_attempt }}
  FORCE_RELEASE: ${{ github.event.inputs.release == 'true' }}

jobs:
  helm-chart-test:
    if: github.event.inputs.skip-test != 'true' && contains(toJson(github.event.commits), '[skip test]') == false
    uses: ./.github/workflows/helm-chart-test.yml
    secrets: inherit
    with:
      release: ${{ github.event.inputs.release == 'true' }}

  release:
    needs:
      - helm-chart-test
    if: (!failure() && !cancelled())
    runs-on: ubuntu-24.04
    permissions: write-all
    env:
      NAME: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
    steps:
      - name: Checkout
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up environment for building chart
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: make setup_dev_env
      - name: Build Helm chart
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 5
          max_attempts: 3
          retry_wait_seconds: 10
          command: |
            SET_VERSION=false make chart_build
            echo "CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $GITHUB_ENV
            echo "CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $GITHUB_ENV

      - name: Configure Git
        run: |
          git config user.name "$GITHUB_ACTOR"
          git config user.email "$GITHUB_ACTOR@users.noreply.github.com"

      - name: Get chart release notes (chart_release_notes.md)
        run: |
          ./generate_chart_changelog.sh
          if [ "${FORCE_RELEASE}" == "true" ]; then
            echo "IS_RELEASE=true" >> $GITHUB_ENV
          else
            echo "IS_RELEASE=$(cat /tmp/selenium_chart_release)" >> $GITHUB_ENV
          fi

      - name: Login Docker Hub
        run: helm registry login registry-1.docker.io -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}

      - name: Push Helm chart to registry
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 120
          command: |
            make chart_release
            echo "LATEST_CHART_VERSION=$(cat /tmp/latest_chart_version)" >> $GITHUB_ENV

      - name: Run chart-releaser
        if: env.IS_RELEASE == 'true'
        uses: helm/chart-releaser-action@main
        with:
          mark_as_latest: false
          skip_existing: false
        env:
          CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
          CR_RELEASE_NOTES_FILE: RELEASE_NOTES.md

      - name: Commit files
        if: github.event.inputs.skip-commit != 'true'
        run: |
          git config --local user.email "selenium-ci@users.noreply.github.com"
          git config --local user.name "Selenium CI Bot"
          git commit -m "[ci] Update chart ${LATEST_CHART_VERSION} changelog" -m "[skip ci]" -a || true
          git pull --rebase

      - name: Push changes
        if: github.event.inputs.skip-commit != 'true'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.SELENIUM_CI_TOKEN || secrets.GITHUB_TOKEN }}
          branch: trunk

  rerun-workflow-when-failure:
    name: Rerun workflow when failure
    needs:
      - helm-chart-test
    if: failure() && ( github.run_attempt < 3 )
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
      - name: Install GitHub CLI
        run: |
          sudo apt update
          sudo apt install gh
      - name: Authenticate GitHub CLI for PR
        if: github.event_name == 'pull_request'
        run: |
          echo "$GH_CLI_TOKEN_PR" | gh auth login --with-token
      - name: Authenticate GitHub CLI
        if: github.event_name != 'pull_request'
        run: |
          echo "$GH_CLI_TOKEN" | gh auth login --with-token
      - name: Rerun workflow when failure
        run: |
          echo "Rerun workflow ID $RUN_ID in attempt #$(($RUN_ATTEMPT + 1))"
          gh workflow run rerun-failed.yml \
            --repo $GITHUB_REPOSITORY \
            --raw-field runId=$RUN_ID \
            --raw-field rerunFailedOnly=$RERUN_FAILED_ONLY


================================================
FILE: .github/workflows/helm-chart-test.yml
================================================
name: Test Helm Charts

on:
  workflow_call:
    secrets:
      DOCKER_USERNAME:
        required: false
      DOCKER_PASSWORD:
        required: false
    inputs:
      release:
        description: 'Test a new release process'
        required: false
        type: string
        default: 'false'
      test-patched-keda:
        description: 'Test patched KEDA (true/false)'
        required: false
        default: ''
        type: string
  workflow_dispatch:
    inputs:
      request-timeout:
        description: 'Test parameter for different request timeout'
        required: false
        default: '15'
      max-replicas-count:
        description: 'Test parameter for autoscaling to set maxReplicaCount'
        required: false
        default: '10'
      log-level:
        description: 'Test parameter for different log level'
        required: false
        default: 'FINE'
      test-patched-keda:
        description: 'Test patched KEDA (true/false)'
        required: false
        default: ''
        type: string

permissions:
  contents: read

jobs:
  build-and-test:
    name: Test K8s
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - k8s-version: 'v1.34.2'
            cluster: 'minikube'
            helm-version: 'v3.19.2'
            docker-version: '29.1.1'
            python-version: '3.10'
            test-upgrade: true
            service-mesh: false
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: disabled
          - k8s-version: 'v1.34.2'
            cluster: 'minikube'
            helm-version: 'v3.18.6'
            docker-version: '29.1.1'
            python-version: '3.10'
            test-upgrade: true
            service-mesh: false
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: job
          - k8s-version: 'v1.34.2'
            cluster: 'minikube'
            helm-version: 'v4.0.1'
            docker-version: '29.1.1'
            python-version: '3.14'
            test-upgrade: true
            service-mesh: true
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: deployment
          - k8s-version: 'v1.29.15'
            cluster: 'minikube'
            helm-version: 'v3.14.3'
            docker-version: '27.5.1'
            python-version: '3.11'
            test-upgrade: true
            service-mesh: false
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: job_https
          - k8s-version: 'v1.30.14'
            cluster: 'minikube'
            helm-version: 'v3.15.4'
            docker-version: '27.5.1'
            python-version: '3.12'
            test-upgrade: true
            service-mesh: false
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: job_hostname
          - k8s-version: 'v1.31.14'
            cluster: 'minikube'
            helm-version: 'v3.16.4'
            docker-version: '27.5.1'
            python-version: '3.13'
            test-upgrade: true
            service-mesh: false
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: deployment_https
          - k8s-version: 'v1.32.10'
            cluster: 'minikube'
            helm-version: 'v3.17.4'
            docker-version: '28.5.2'
            python-version: '3.10'
            test-upgrade: true
            service-mesh: false
            os: ubuntu-22.04
            check-records-output: true
            test-strategy: playwright_connect_grid
          - k8s-version: 'v1.33.6'
            cluster: 'minikube'
            helm-version: 'v3.18.6'
            docker-version: '28.5.2'
            python-version: '3.10'
            test-upgrade: true
            service-mesh: true
            os: ubuntu-22.04
            check-records-output: false
            test-strategy: job_relay
    env:
      CLUSTER: ${{ matrix.cluster }}
      KUBERNETES_VERSION: ${{ matrix.k8s-version }}
      ARTIFACT_NAME: "${{ matrix.k8s-version }}-${{ matrix.test-strategy }}"
      HELM_VERSION: ${{ matrix.helm-version }}
      DOCKER_VERSION: ${{ matrix.docker-version }}
      TEST_UPGRADE_CHART: ${{ matrix.test-upgrade }}
      SERVICE_MESH: ${{ matrix.service-mesh }}
      CHECK_RECORD_OUTPUT: ${{ matrix.check-records-output }}
      SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
      SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
      SAUCE_REGION: ${{ secrets.SAUCE_REGION }}
      TEST_PATCHED_KEDA: ${{ github.event.inputs.test-patched-keda }}
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          large-packages: false
      - name: Checkout code
        uses: actions/checkout@main
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set up Python
        uses: actions/setup-python@main
        with:
          python-version: ${{ matrix.python-version }}
          check-latest: true
      - name: Get branch name (only for push to branch)
        if: github.event_name == 'push'
        run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV
        env:
          PUSH_BRANCH: ${{ github.ref }}
      - name: Get target branch name (only for PRs)
        if: github.event_name == 'pull_request'
        run: echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV
        env:
          TARGET_BRANCH: ${{ github.head_ref }}
      - name: Output branch name
        run: echo ${BRANCH}
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ inputs.release || false }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          echo "AUTHORS=${AUTHORS}" >> $GITHUB_ENV
        env:
          AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
      - name: Build Helm charts
        run: |
          BUILD_DATE=${BUILD_DATE} make chart_build
          echo "CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $GITHUB_ENV
          echo "CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $GITHUB_ENV
      - name: Build Docker images
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 90
          max_attempts: 3
          retry_wait_seconds: 60
          command: NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" || true
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Setup Kubernetes cluster
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: CLUSTER=${CLUSTER} SERVICE_MESH=${SERVICE_MESH} KUBERNETES_VERSION=${KUBERNETES_VERSION} NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make chart_cluster_setup
      - name: Test chart template
        run: NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make chart_test_template
      - name: Test set custom CA certificate
        run: NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make test_custom_ca_cert
      - name: Set test parameters
        if: (matrix.test-strategy == 'job' || matrix.test-strategy == 'deployment') && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
        run: |
          echo "AUTOSCALING_POLL_INTERVAL=${AUTOSCALING_POLL_INTERVAL}" >> $GITHUB_ENV
          echo "SET_MAX_REPLICAS=${SET_MAX_REPLICAS}" >> $GITHUB_ENV
          echo "LOG_LEVEL=${LOG_LEVEL}" >> $GITHUB_ENV
        env:
          AUTOSCALING_POLL_INTERVAL: ${{ github.event.inputs.request-timeout || '15' }}
          SET_MAX_REPLICAS: ${{ github.event.inputs.max-replicas-count || '10' }}
          LOG_LEVEL: ${{ github.event.inputs.log-level || 'FINE' }}
      - name: Test Selenium Grid on Kubernetes ${{ matrix.k8s-version }} with Autoscaling ${{ matrix.test-strategy }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 30
          max_attempts: 3
          command: |
            NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} TEST_UPGRADE_CHART=false make chart_test_autoscaling_${{ matrix.test-strategy }}
            exit_code=$?
            if [[ "${CHECK_RECORD_OUTPUT}" = "true" ]] && [[ "${exit_code}" -eq 0 ]]; then
              NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make test_video_integrity
              exit_code=$?
            fi
            exit ${exit_code}
      - name: Upload Helm chart package
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: "${{ env.ARTIFACT_NAME }}_${{ env.CHART_FILE_NAME }}_${{ matrix.os }}"
          path: ${{ env.CHART_PACKAGE_PATH }}
      - name: Upload chart test artifacts
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: ${{ env.ARTIFACT_NAME }}-artifacts
          path: ./tests/tests/
          if-no-files-found: ignore
      - name: Upload test video artifacts
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: ${{ env.ARTIFACT_NAME }}-videos
          path: |
            ./tests/videos/
            !./tests/videos/**/*.hprof
      - name: Test chart upgrade
        if: (matrix.test-upgrade == true)
        run: |
          NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} SET_MAX_REPLICAS=10 TEST_NAME_OVERRIDE=true TEST_UPGRADE_CHART=${TEST_UPGRADE_CHART} SET_UPDATE_STRATEGY=Recreate make chart_test_autoscaling_${{ matrix.test-strategy }}
      - name: Cleanup Kubernetes cluster
        if: always()
        run: CLUSTER=${CLUSTER} make chart_cluster_cleanup
      - name: Clean up Docker
        if: always()
        run: docker system prune -af


================================================
FILE: .github/workflows/k8s-scaling-test.yml
================================================
name: Test Grid Autoscaling in Kubernetes

on:
  workflow_call:
    secrets:
      DOCKER_USERNAME:
        required: false
      DOCKER_PASSWORD:
        required: false
    inputs:
      release:
        description: 'Test a new release process'
        required: false
        type: string
        default: 'false'
      push-results:
        description: 'Publish the results to the repository'
        required: false
        type: boolean
        default: false
      iteration:
        description: 'Test a specific iteration'
        required: false
        type: string
        default: '20'
      test-patched-keda:
        description: 'Test patched KEDA (true/false)'
        required: false
        default: ''
        type: string
  workflow_dispatch:
    inputs:
      publish-results:
        description: 'Publish the results to the repository'
        required: false
        type: boolean
        default: false
      pr-results:
        description: 'Create a PR with the results'
        required: false
        type: boolean
        default: false
      iteration:
        description: 'Test a specific iteration'
        required: false
        type: string
        default: '20'
      test-patched-keda:
        description: 'Test patched KEDA (true/false)'
        required: false
        default: ''
        type: string

concurrency:
  group: ${{ github.workflow }}-${{ github.ref == github.ref_protected && github.run_id || github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions:
  contents: write
  pull-requests: write

env:
  RUN_ID: ${{ github.run_id }}
  TEST_AUTOSCALING_ITERATIONS: ${{ github.event.inputs.iteration || '20' }}

jobs:
  build-and-test:
    name: Test K8s
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - k8s-version: 'v1.34.2'
            cluster: 'minikube'
            helm-version: 'v4.0.1'
            docker-version: '29.1.1'
            python-version: '3.13'
            os: ubuntu-22.04
            test-strategy: test_k8s_autoscaling_job_count_strategy_default_in_chaos
          - k8s-version: 'v1.34.2'
            cluster: 'minikube'
            helm-version: 'v3.19.2'
            docker-version: '29.1.1'
            python-version: '3.13'
            os: ubuntu-22.04
            test-strategy: test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions
          - k8s-version: 'v1.33.6'
            cluster: 'minikube'
            helm-version: 'v3.19.2'
            docker-version: '29.1.1'
            python-version: '3.12'
            os: ubuntu-22.04
            test-strategy: test_k8s_autoscaling_job_count_strategy_default
          - k8s-version: 'v1.30.14'
            cluster: 'minikube'
            helm-version: 'v3.15.4'
            docker-version: '27.5.1'
            python-version: '3.12'
            os: ubuntu-22.04
            test-strategy: test_k8s_autoscaling_deployment_count_in_chaos
          - k8s-version: 'v1.31.14'
            cluster: 'minikube'
            helm-version: 'v3.16.4'
            docker-version: '28.5.2'
            python-version: '3.13'
            os: ubuntu-22.04
            test-strategy: test_k8s_autoscaling_deployment_count_with_node_max_sessions
          - k8s-version: 'v1.32.10'
            cluster: 'minikube'
            helm-version: 'v3.17.4'
            docker-version: '28.5.2'
            python-version: '3.11'
            os: ubuntu-22.04
            test-strategy: test_k8s_autoscaling_deployment_count
    env:
      CLUSTER: ${{ matrix.cluster }}
      KUBERNETES_VERSION: ${{ matrix.k8s-version }}
      HELM_VERSION: ${{ matrix.helm-version }}
      DOCKER_VERSION: ${{ matrix.docker-version }}
      TEST_PATCHED_KEDA: ${{ github.event.inputs.test-patched-keda }}
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: false
          large-packages: false
      - name: Checkout code
        uses: actions/checkout@main
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set up Python
        uses: actions/setup-python@main
        with:
          python-version: ${{ matrix.python-version }}
          check-latest: true
      - name: Get branch name (only for push to branch)
        if: github.event_name == 'push'
        run: echo "BRANCH=$(echo ${PUSH_BRANCH##*/})" >> $GITHUB_ENV
        env:
          PUSH_BRANCH: ${{ github.ref }}
      - name: Get target branch name (only for PRs)
        if: github.event_name == 'pull_request'
        run: echo "BRANCH=$(echo ${TARGET_BRANCH##*/})" >> $GITHUB_ENV
        env:
          TARGET_BRANCH: ${{ github.head_ref }}
      - name: Output branch name
        run: echo ${BRANCH}
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ inputs.release || false }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          echo "AUTHORS=${AUTHORS}" >> $GITHUB_ENV
        env:
          AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Setup Kubernetes cluster
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: CLUSTER=${CLUSTER} SERVICE_MESH=${SERVICE_MESH} KUBERNETES_VERSION=${KUBERNETES_VERSION} NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make chart_cluster_setup
      - name: Build Docker images
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 30
          max_attempts: 3
          retry_wait_seconds: 60
          command: NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} make build
      - name: Build Helm charts
        run: |
          BUILD_DATE=${BUILD_DATE} make chart_build
          echo "CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $GITHUB_ENV
          echo "CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $GITHUB_ENV
      - name: Test Selenium Grid on Kubernetes with Autoscaling
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 30
          max_attempts: 3
          command: |
            NAME=${IMAGE_REGISTRY} VERSION=${BRANCH} BUILD_DATE=${BUILD_DATE} TEST_UPGRADE_CHART=false TEST_AUTOSCALING_ITERATIONS=${TEST_AUTOSCALING_ITERATIONS} \
            make ${{ matrix.test-strategy }}
      - name: Rename results
        run: mv ./tests/tests/autoscaling_results.md ./tests/tests/results_${{ matrix.test-strategy }}.md
      - name: Upload results
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: results_${{ matrix.test-strategy }}
          path: ./tests/tests/results_${{ matrix.test-strategy }}.md
          if-no-files-found: ignore
      - name: Cleanup Kubernetes cluster
        if: always()
        run: CLUSTER=${CLUSTER} make chart_cluster_cleanup
      - name: Clean up Docker
        if: always()
        run: docker system prune -af

  publish-results:
    name: Publish Results
    if: (!failure() && !cancelled() && (github.event.inputs.publish-results == 'true'))
    runs-on: ubuntu-24.04
    needs: build-and-test
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Download results
        uses: actions/download-artifact@v8
        with:
          path: ./.keda
          pattern: 'results_*'
          merge-multiple: 'true'
          run-id: ${{ env.RUN_ID }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
      - name: Commit files
        run: |
          git config --local user.email "selenium-ci@users.noreply.github.com"
          git config --local user.name "Selenium CI Bot"
          git add .keda/.
          git commit -m "[ci] Upload autoscaling in K8s test results" -m "[skip ci]" -a
          git pull --rebase
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.SELENIUM_CI_TOKEN }}
          branch: ${{ env.BRANCH_NAME }}
        env:
          BRANCH_NAME: ${{ github.head_ref || github.ref_name }}


  pr-results:
    name: Create a PR with the results
    if: (!failure() && !cancelled() && (github.event.inputs.pr-results == 'true'))
    runs-on: ubuntu-24.04
    needs: build-and-test
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Download results
        uses: actions/download-artifact@v8
        with:
          path: ./.keda
          pattern: 'results_*'
          merge-multiple: 'true'
          run-id: ${{ env.RUN_ID }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
      - name: Commit configs
        run: |
          git config --local user.email "selenium-ci@users.noreply.github.com"
          git config --local user.name "Selenium CI Bot"
      - name: Create Pull Request
        id: cpr
        uses: peter-evans/create-pull-request@main
        with:
          token: ${{ secrets.SELENIUM_CI_TOKEN }}
          commit-message: "[ci] Upload autoscaling in K8s test results"
          title: "[ci] Upload autoscaling in K8s test results"
          body: "This PR contains the results of the autoscaling tests in Kubernetes"
          committer: 'Selenium CI Bot <selenium-ci@users.noreply.github.com>'
          author: 'Selenium CI Bot <selenium-ci@users.noreply.github.com>'
          branch: autoscaling-results
      - name: Check outputs
        if: ${{ steps.cpr.outputs.pull-request-number }}
        run: |
          echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
          echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"


================================================
FILE: .github/workflows/label-commenter.yml
================================================
# Configuration for Label Commenter - https://github.com/peaceiris/actions-label-commenter
name: Label Commenter

on:
  issues:
    types: [ labeled ]

permissions:
  contents: read
  issues: write

jobs:
  comment:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@main
      - name: Label Commenter
        uses: peaceiris/actions-label-commenter@v1


================================================
FILE: .github/workflows/lock.yml
================================================
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
name: 'Lock Issues'

on:
  workflow_dispatch:
  schedule:
    - cron: '0 23 * * *'

permissions:
  issues: write
  pull-requests: write

jobs:
  action:
    runs-on: ubuntu-24.04
    steps:
      - uses: dessant/lock-threads@v6
        with:
          process-only: 'issues'
          issue-inactive-days: '30'
          issue-lock-reason: ''
          issue-comment: >
            This issue has been automatically locked since there
            has not been any recent activity after it was closed.
            Please open a new issue for related bugs.


================================================
FILE: .github/workflows/nightly.yml
================================================
name: Nightly
on:
  workflow_dispatch:
    inputs:
      skip-test:
        description: 'Skip the tests'
        required: false
        type: boolean
        default: false
  schedule:
    - cron: '0 1 * * *'

jobs:
  build-test:
    name: Build and Test Nightly
    if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.skip-test == 'false')
    uses: ./.github/workflows/build-test.yml
    secrets: inherit
    with:
      release: false

  deploy:
    needs:
      - build-test
    if: (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && !failure() && !cancelled()
    name: Deploy and Release Nightly
    runs-on: ubuntu-24.04
    permissions: write-all
    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@main
        with:
          tool-cache: true
          android: true
          dotnet: true
          haskell: true
          large-packages: true
          docker-images: true
          swap-storage: true
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: false
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          make set_build_multiarch
          cat .env | xargs -I {} echo {} >> $GITHUB_ENV
      - name: Sets prerelease to nightly
        run: |
          echo "PRERELEASE=true" >> $GITHUB_ENV
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          echo "AUTHORS=${AUTHORS}" >> $GITHUB_ENV
        env:
          NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
          AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
      - name: Get Grid version
        run: |
          echo ${BASE_VERSION}
          echo "GRID_VERSION=${BASE_VERSION}" >> $GITHUB_ENV
      - name: Display Grid version and set Base version
        run: |
          echo ${GRID_VERSION}
          echo "BASE_RELEASE=nightly" >> $GITHUB_ENV
      - name: Update tag nightly
        uses: richardsimko/update-tag@v1.1.6
        with:
          tag_name: ${{ env.BASE_RELEASE }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Build images
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 180
          max_attempts: 3
          retry_wait_seconds: 60
          command: PLATFORMS="${PLATFORMS}" VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make build
      - name: Login Docker Hub
        run: |
          docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
          helm registry login registry-1.docker.io -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Tag images as nightly
        run: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make tag_nightly
      - name: Deploy nightly tag
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 120
          command: VERSION="${GRID_VERSION}" BUILD_DATE=${BUILD_DATE} make release_nightly
#      - name: Update package versions
#        run: make generate_nightly_sbom
      - name: Get current latest tag
        run: echo "LATEST_TAG=$(git describe --tags --abbrev=0 --exclude=nightly --exclude=selenium-grid*)" >> $GITHUB_ENV
      - name: Display latest tag
        run: echo ${LATEST_TAG}
      - name: Sets env var for nightly tag
        run: |
          echo "NEXT_TAG=nightly" >> $GITHUB_ENV
          echo "FILTER_IMAGE_TAG=nightly" >> $GITHUB_ENV
      - name: Create release notes (release_notes.md)
        run: ./generate_release_notes.sh ${LATEST_TAG} origin/trunk ${GRID_VERSION} ${BUILD_DATE}
      - name: Set up Python
        uses: actions/setup-python@main
        with:
          python-version: '3.14'
          check-latest: true
      - name: Update tag in docs and files
        run: ./update_tag_in_docs_and_files.sh ${LATEST_TAG} ${NEXT_TAG}
      - name: Delete previous nightly tag if any
        uses: cb80/delrel@main
        with:
          tag: ${{ env.BASE_RELEASE }}
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Build Helm chart
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 5
          max_attempts: 3
          retry_wait_seconds: 10
          command: |
            make chart_build_nightly
            echo "CHART_PACKAGE_PATH=$(cat /tmp/selenium_chart_version)" >> $GITHUB_ENV
            echo "CHART_FILE_NAME=$(basename $(cat /tmp/selenium_chart_version))" >> $GITHUB_ENV
      - name: Push Helm chart to registry
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 120
          command: make chart_release
      - name: Create Nightly Release
        id: create_release
        uses: softprops/action-gh-release@v2.6.1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          tag_name: ${{ env.BASE_RELEASE }}
          name: "Nightly"
          body_path: "release_notes.md"
          files: |
            package_versions.txt
            ${{ env.CHART_PACKAGE_PATH }}
          generate_release_notes: true
          draft: false
          prerelease: true
          append_body: false


================================================
FILE: .github/workflows/release-all-browser-versions.yml
================================================
name: Deploy all browser versions

on:
  workflow_dispatch:
    inputs:
      stable:
        description: 'Use upstream stable build'
        required: true
        type: string
        default: 'true'
      reuse-base:
        description: 'Reuse base image to build'
        required: false
        type: boolean
        default: true
      grid-version:
        description: 'Grid version to build. E.g: 4.28.1. Must provide if reusing base image'
        required: false
        type: string
        default: ''
      push-image:
        description: 'Push image after testing successfully'
        required: true
        type: boolean
        default: false
      pr-changelog:
        description: 'Create a PR for CHANGELOG'
        required: true
        type: boolean
        default: true

jobs:
  dispatch-chrome:
    runs-on: ubuntu-latest
    steps:
      - name: Dispatch Chrome versions
        uses: actions/github-script@v8
        with:
          script: |
            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'release-chrome-versions.yml',
              ref: context.ref,
              inputs: {
                'stable': '${{ github.event.inputs.stable }}',
                'reuse-base': '${{ github.event.inputs.reuse-base }}',
                'grid-version': '${{ github.event.inputs.grid-version }}',
                'push-image': '${{ github.event.inputs.push-image }}',
                'pr-changelog': '${{ github.event.inputs.pr-changelog }}'
              }
            });

  dispatch-chrome-for-testing:
    runs-on: ubuntu-latest
    steps:
      - name: Dispatch Chrome for Testing versions
        uses: actions/github-script@v8
        with:
          script: |
            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'release-chrome-for-testing-versions.yml',
              ref: context.ref,
              inputs: {
                'stable': '${{ github.event.inputs.stable }}',
                'reuse-base': '${{ github.event.inputs.reuse-base }}',
                'grid-version': '${{ github.event.inputs.grid-version }}',
                'push-image': '${{ github.event.inputs.push-image }}',
                'pr-changelog': '${{ github.event.inputs.pr-changelog }}'
              }
            });

  dispatch-edge:
    runs-on: ubuntu-latest
    steps:
      - name: Dispatch Edge versions
        uses: actions/github-script@v8
        with:
          script: |
            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'release-edge-versions.yml',
              ref: context.ref,
              inputs: {
                'stable': '${{ github.event.inputs.stable }}',
                'reuse-base': '${{ github.event.inputs.reuse-base }}',
                'grid-version': '${{ github.event.inputs.grid-version }}',
                'push-image': '${{ github.event.inputs.push-image }}',
                'pr-changelog': '${{ github.event.inputs.pr-changelog }}'
              }
            });

  dispatch-firefox:
    runs-on: ubuntu-latest
    steps:
      - name: Dispatch Firefox versions
        uses: actions/github-script@v8
        with:
          script: |
            await github.rest.actions.createWorkflowDispatch({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'release-firefox-versions.yml',
              ref: context.ref,
              inputs: {
                'stable': '${{ github.event.inputs.stable }}',
                'reuse-base': '${{ github.event.inputs.reuse-base }}',
                'grid-version': '${{ github.event.inputs.grid-version }}',
                'push-image': '${{ github.event.inputs.push-image }}',
                'pr-changelog': '${{ github.event.inputs.pr-changelog }}'
              }
            });

================================================
FILE: .github/workflows/release-chrome-for-testing-versions.yml
================================================
name: Deploy specific Chrome for Testing version

on:
  workflow_dispatch:
    inputs:
      stable:
        description: 'Use upstream stable build'
        required: true
        type: string
        default: 'true'
      reuse-base:
        description: 'Reuse base image to build'
        required: false
        type: boolean
        default: true
      grid-version:
        description: 'Grid version to build. E.g: 4.28.1. Must provide if reusing base image'
        required: false
        type: string
        default: ''
      build-date:
        description: 'Build date in format YYYYMMDD. Must provide if reusing base image'
        required: false
        type: string
        default: '20260222'
      browser-name:
        description: 'Browser name to build. E.g: chrome-for-testing'
        required: true
        type: string
        default: 'chrome-for-testing'
      browser-versions:
        description: 'List browser version to build. E.g: [130, 131]'
        required: true
        default: '[113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145]'
      push-image:
        description: 'Push image after testing successfully'
        required: true
        type: boolean
        default: false
      pr-changelog:
        description: 'Create a PR for CHANGELOG'
        required: true
        type: boolean
        default: true

env:
  GRID_VERSION: ${{ github.event.inputs.grid-version }}
  BROWSER_NAME: ${{ github.event.inputs.browser-name }}
  REUSE_BASE: ${{ github.event.inputs.reuse-base || true }}
  BUILD_DATE: ${{ github.event.inputs.build-date || '' }}
  NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
  AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
  PUSH_IMAGE: ${{ github.event.inputs.push-image || false }}
  PR_CHANGELOG: ${{ github.event.inputs.pr-changelog || true }}
  RUN_ID: ${{ github.run_id }}

jobs:
  deploy:
    name: Node/Standalone Chrome for Testing
    runs-on: ubuntu-24.04
    permissions: write-all
    strategy:
      fail-fast: false
      max-parallel: 10
      matrix:
        browser-version: ${{ fromJSON(github.event.inputs.browser-versions)}}
    outputs:
      GRID_VERSION: ${{ steps.display_grid_version.outputs.GRID_VERSION }}
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            INSTALL_DOCKER=false make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ github.event.inputs.stable || true }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          if [ -z "${BUILD_DATE}" ]; then
            echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          else
            echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
          fi
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          echo "BROWSER_VERSION=${BROWSER_VERSION}" >> $GITHUB_ENV
        env:
          BROWSER_VERSION: ${{ matrix.browser-version }}
      - name: Get Grid version
        if: env.GRID_VERSION == ''
        run: |
          echo ${BASE_VERSION}
          echo "GRID_VERSION=${BASE_VERSION}" >> $GITHUB_ENV
      - name: Display Grid version
        id: display_grid_version
        run: |
          echo ${GRID_VERSION}
          echo "GRID_VERSION=${GRID_VERSION}" >> "$GITHUB_OUTPUT"
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Build images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make update_browser_versions_matrix
            ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE}
            EXIT_CODE=$?
            cat .env | xargs -I {} echo {} >> $GITHUB_ENV
            exit $EXIT_CODE
      - name: Build Hub image for testing
        if: env.REUSE_BASE == 'false'
        run: make hub
      - name: Test images Node with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make test_chrome-for-testing
      - name: Test images Standalone with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make test_chrome-for-testing_standalone \
            && make test_chrome-for-testing_standalone_java
      - name: Push images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        if: env.PUSH_IMAGE == 'true'
        run: |
          ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE} true true
      - name: Upload changelog
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: image_tags_${{ env.GRID_VERSION }}_${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}
          path: ./CHANGELOG/${{ env.GRID_VERSION }}/${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}.md
          if-no-files-found: ignore

  pr-results:
    if: (!failure() && !cancelled() && (github.event.inputs.pr-changelog == 'true'))
    uses: ./.github/workflows/create-changelog-pr.yml
    needs: deploy
    with:
      grid-version: ${{ needs.deploy.outputs.GRID_VERSION }}
      browser-name: ${{ github.event.inputs.browser-name }}
      browser-versions: ${{ github.event.inputs.browser-versions }}
      run-id: ${{ github.run_id }}
    secrets: inherit


================================================
FILE: .github/workflows/release-chrome-versions.yml
================================================
name: Deploy specific Chrome version

on:
  workflow_dispatch:
    inputs:
      stable:
        description: 'Use upstream stable build'
        required: true
        type: string
        default: 'true'
      reuse-base:
        description: 'Reuse base image to build'
        required: false
        type: boolean
        default: true
      grid-version:
        description: 'Grid version to build. E.g: 4.28.1. Must provide if reusing base image'
        required: false
        type: string
        default: ''
      build-date:
        description: 'Build date in format YYYYMMDD. Must provide if reusing base image'
        required: false
        type: string
        default: '20260222'
      browser-name:
        description: 'Browser name to build. E.g: chrome'
        required: true
        type: string
        default: 'chrome'
      browser-versions:
        description: 'List browser version to build. E.g: [130, 131]'
        required: true
        default: '[95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145]'
      push-image:
        description: 'Push image after testing successfully'
        required: true
        type: boolean
        default: false
      pr-changelog:
        description: 'Create a PR for CHANGELOG'
        required: true
        type: boolean
        default: true

env:
  GRID_VERSION: ${{ github.event.inputs.grid-version }}
  BROWSER_NAME: ${{ github.event.inputs.browser-name }}
  REUSE_BASE: ${{ github.event.inputs.reuse-base || true }}
  BUILD_DATE: ${{ github.event.inputs.build-date || '' }}
  NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
  AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
  PUSH_IMAGE: ${{ github.event.inputs.push-image || false }}
  PR_CHANGELOG: ${{ github.event.inputs.pr-changelog || true }}
  RUN_ID: ${{ github.run_id }}

jobs:
  deploy:
    name: Node/Standalone Chrome
    runs-on: ubuntu-24.04
    permissions: write-all
    strategy:
      fail-fast: false
      max-parallel: 10
      matrix:
        browser-version: ${{ fromJSON(github.event.inputs.browser-versions)}}
    outputs:
      GRID_VERSION: ${{ steps.display_grid_version.outputs.GRID_VERSION }}
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            INSTALL_DOCKER=false make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ github.event.inputs.stable || true }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          if [ -z "${BUILD_DATE}" ]; then
            echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          else
            echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
          fi
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          echo "BROWSER_VERSION=${BROWSER_VERSION}" >> $GITHUB_ENV
        env:
          BROWSER_VERSION: ${{ matrix.browser-version }}
      - name: Get Grid version
        if: env.GRID_VERSION == ''
        run: |
          echo ${BASE_VERSION}
          echo "GRID_VERSION=${BASE_VERSION}" >> $GITHUB_ENV
      - name: Display Grid version
        id: display_grid_version
        run: |
          echo ${GRID_VERSION}
          echo "GRID_VERSION=${GRID_VERSION}" >> "$GITHUB_OUTPUT"
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Build images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make update_browser_versions_matrix
            ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE}
            EXIT_CODE=$?
            cat .env | xargs -I {} echo {} >> $GITHUB_ENV
            exit $EXIT_CODE
      - name: Build Hub image for testing
        if: env.REUSE_BASE == 'false'
        run: make hub
      - name: Test images Node with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make test_chrome
      - name: Test images Standalone with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make test_chrome_standalone \
            && make test_chrome_standalone_java
      - name: Push images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        if: env.PUSH_IMAGE == 'true'
        run: |
          ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE} true true
      - name: Upload changelog
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: image_tags_${{ env.GRID_VERSION }}_${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}
          path: ./CHANGELOG/${{ env.GRID_VERSION }}/${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}.md
          if-no-files-found: ignore

  pr-results:
    if: (!failure() && !cancelled() && (github.event.inputs.pr-changelog == 'true'))
    uses: ./.github/workflows/create-changelog-pr.yml
    needs: deploy
    with:
      grid-version: ${{ needs.deploy.outputs.GRID_VERSION }}
      browser-name: ${{ github.event.inputs.browser-name }}
      browser-versions: ${{ github.event.inputs.browser-versions }}
      run-id: ${{ github.run_id }}
    secrets: inherit


================================================
FILE: .github/workflows/release-edge-versions.yml
================================================
name: Deploy specific Edge version

on:
  workflow_dispatch:
    inputs:
      stable:
        description: 'Use upstream stable build'
        required: true
        type: string
        default: 'true'
      reuse-base:
        description: 'Reuse base image to build'
        required: false
        type: boolean
        default: true
      grid-version:
        description: 'Grid version to build. E.g: 4.28.1. Must provide if reusing base image'
        required: false
        type: string
        default: ''
      build-date:
        description: 'Build date in format YYYYMMDD. Must provide if reusing base image'
        required: false
        type: string
        default: '20260222'
      browser-name:
        description: 'Browser name to build. E.g: edge'
        required: true
        type: string
        default: 'edge'
      browser-versions:
        description: 'List browser version to build. E.g: [130, 131]'
        required: true
        default: '[114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145]'
      push-image:
        description: 'Push image after testing successfully'
        required: true
        type: boolean
        default: false
      pr-changelog:
        description: 'Create a PR for CHANGELOG'
        required: true
        type: boolean
        default: true

env:
  GRID_VERSION: ${{ github.event.inputs.grid-version }}
  BROWSER_NAME: ${{ github.event.inputs.browser-name }}
  REUSE_BASE: ${{ github.event.inputs.reuse-base || true }}
  BUILD_DATE: ${{ github.event.inputs.build-date || '' }}
  NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
  AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
  PUSH_IMAGE: ${{ github.event.inputs.push-image || false }}
  PR_CHANGELOG: ${{ github.event.inputs.pr-changelog || true }}
  RUN_ID: ${{ github.run_id }}

jobs:
  deploy:
    name: Node/Standalone Edge
    runs-on: ubuntu-24.04
    permissions: write-all
    strategy:
      fail-fast: false
      max-parallel: 10
      matrix:
        browser-version: ${{ fromJSON(github.event.inputs.browser-versions)}}
    outputs:
      GRID_VERSION: ${{ steps.display_grid_version.outputs.GRID_VERSION }}
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            INSTALL_DOCKER=false make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ github.event.inputs.stable || true }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          if [ -z "${BUILD_DATE}" ]; then
            echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          else
            echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
          fi
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          echo "BROWSER_VERSION=${BROWSER_VERSION}" >> $GITHUB_ENV
        env:
          BROWSER_VERSION: ${{ matrix.browser-version }}
      - name: Get Grid version
        if: env.GRID_VERSION == ''
        run: |
          echo ${BASE_VERSION}
          echo "GRID_VERSION=${BASE_VERSION}" >> $GITHUB_ENV
      - name: Display Grid version
        id: display_grid_version
        run: |
          echo ${GRID_VERSION}
          echo "GRID_VERSION=${GRID_VERSION}" >> "$GITHUB_OUTPUT"
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Build images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make update_browser_versions_matrix
            ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE}
            EXIT_CODE=$?
            cat .env | xargs -I {} echo {} >> $GITHUB_ENV
            exit $EXIT_CODE
      - name: Build Hub image for testing
        if: env.REUSE_BASE == 'false'
        run: make hub
      - name: Test images Node with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make test_edge
      - name: Test images Standalone with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make test_edge_standalone \
            && make test_edge_standalone_java
      - name: Push images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        if: env.PUSH_IMAGE == 'true'
        run: |
          ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE} true true
      - name: Upload changelog
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: image_tags_${{ env.GRID_VERSION }}_${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}
          path: ./CHANGELOG/${{ env.GRID_VERSION }}/${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}.md
          if-no-files-found: ignore

  pr-results:
    if: (!failure() && !cancelled() && (github.event.inputs.pr-changelog == 'true'))
    uses: ./.github/workflows/create-changelog-pr.yml
    needs: deploy
    with:
      grid-version: ${{ needs.deploy.outputs.GRID_VERSION }}
      browser-name: ${{ github.event.inputs.browser-name }}
      browser-versions: ${{ github.event.inputs.browser-versions }}
      run-id: ${{ github.run_id }}
    secrets: inherit


================================================
FILE: .github/workflows/release-firefox-versions.yml
================================================
name: Deploy specific Firefox version

on:
  workflow_dispatch:
    inputs:
      stable:
        description: 'Use upstream stable build'
        required: true
        type: string
        default: 'true'
      reuse-base:
        description: 'Reuse base image to build'
        required: false
        type: boolean
        default: true
      grid-version:
        description: 'Grid version to build. E.g: 4.28.1. Must provide if reusing base image'
        required: false
        type: string
        default: ''
      build-date:
        description: 'Build date in format YYYYMMDD. Must provide if reusing base image'
        required: false
        type: string
        default: '20260222'
      browser-name:
        description: 'Browser name to build. E.g: firefox'
        required: true
        type: string
        default: 'firefox'
      browser-versions:
        description: 'List browser version to build. E.g: [130, 131]'
        required: true
        default: '[98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148]'
      push-image:
        description: 'Push image after testing successfully'
        required: true
        type: boolean
        default: false
      pr-changelog:
        description: 'Create a PR for CHANGELOG'
        required: true
        type: boolean
        default: true

env:
  GRID_VERSION: ${{ github.event.inputs.grid-version }}
  BROWSER_NAME: ${{ github.event.inputs.browser-name }}
  REUSE_BASE: ${{ github.event.inputs.reuse-base || true }}
  BUILD_DATE: ${{ github.event.inputs.build-date || '' }}
  NAMESPACE: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
  AUTHORS: ${{ vars.AUTHORS || 'SeleniumHQ' }}
  PUSH_IMAGE: ${{ github.event.inputs.push-image || false }}
  PR_CHANGELOG: ${{ github.event.inputs.pr-changelog || true }}
  RUN_ID: ${{ github.run_id }}

jobs:
  deploy:
    name: Node/Standalone Firefox
    runs-on: ubuntu-24.04
    permissions: write-all
    strategy:
      fail-fast: false
      max-parallel: 10
      matrix:
        browser-version: ${{ fromJSON(github.event.inputs.browser-versions)}}
    outputs:
      GRID_VERSION: ${{ steps.display_grid_version.outputs.GRID_VERSION }}
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up containerd image store feature
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            INSTALL_DOCKER=true make setup_dev_env
      - name: Output Docker info
        run: docker info
      - name: Set Selenium base version
        uses: ./.github/actions/get-latest-upstream
        with:
          release: ${{ github.event.inputs.stable || true }}
          gh_cli_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Sets build date
        run: |
          if [ -z "${BUILD_DATE}" ]; then
            echo "BUILD_DATE=$(date '+%Y%m%d')" >> $GITHUB_ENV
          else
            echo "BUILD_DATE=${BUILD_DATE}" >> $GITHUB_ENV
          fi
          echo "NAME=${NAMESPACE}" >> $GITHUB_ENV
          echo "BROWSER_VERSION=${BROWSER_VERSION}" >> $GITHUB_ENV
        env:
          BROWSER_VERSION: ${{ matrix.browser-version }}
      - name: Get Grid version
        if: env.GRID_VERSION == ''
        run: |
          echo ${BASE_VERSION}
          echo "GRID_VERSION=${BASE_VERSION}" >> $GITHUB_ENV
      - name: Display Grid version
        id: display_grid_version
        run: |
          echo ${GRID_VERSION}
          echo "GRID_VERSION=${GRID_VERSION}" >> "$GITHUB_OUTPUT"
      - name: Login Docker Hub
        run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
        env:
          DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
          DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      - name: Build images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 90
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            make update_browser_versions_matrix
            ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE}
            EXIT_CODE=$?
            cat .env | xargs -I {} echo {} >> $GITHUB_ENV
            exit $EXIT_CODE
      - name: Build Hub image for testing
        if: env.REUSE_BASE == 'false'
        run: make hub
      - name: Test images Node with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            TEST_FIREFOX_INSTALL_LANG_PACKAGE=false FIREFOX_VERSION=${FIREFOX_VERSION} make test_firefox
      - name: Test images Standalone with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 20
          max_attempts: 3
          retry_wait_seconds: 60
          command: |
            FIREFOX_VERSION=${FIREFOX_VERSION} make test_firefox_standalone \
            && make test_firefox_standalone_java
      - name: Push images with Grid core ${{ env.GRID_VERSION }} and ${{ env.BROWSER_NAME }} v${{ env.BROWSER_VERSION }}
        if: env.PUSH_IMAGE == 'true'
        run: |
          ./tests/build-backward-compatible/bootstrap.sh ${GRID_VERSION} ${BROWSER_VERSION} ${BROWSER_NAME} ${REUSE_BASE} true true
      - name: Upload changelog
        if: always()
        uses: actions/upload-artifact@main
        with:
          name: image_tags_${{ env.GRID_VERSION }}_${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}
          path: ./CHANGELOG/${{ env.GRID_VERSION }}/${{ env.BROWSER_NAME }}_${{ env.BROWSER_VERSION }}.md
          if-no-files-found: ignore

  pr-results:
    if: (!failure() && !cancelled() && (github.event.inputs.pr-changelog == 'true'))
    uses: ./.github/workflows/create-changelog-pr.yml
    needs: deploy
    with:
      grid-version: ${{ needs.deploy.outputs.GRID_VERSION }}
      browser-name: ${{ github.event.inputs.browser-name }}
      browser-versions: ${{ github.event.inputs.browser-versions }}
      run-id: ${{ github.run_id }}
    secrets: inherit


================================================
FILE: .github/workflows/release-preparation.yml
================================================
name: Release Preparation

on:
  workflow_call:
    inputs:
      grid-version:
        required: true
        default: '4.37.0'
        type: string
  workflow_dispatch:
    inputs:
      grid-version:
        required: true
        type: string
        default: '4.37.0'
        description: Expected Grid version to update

jobs:
  pr-results:
    name: Create a PR with changelog
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Check existing PR
        id: check-pr
        run: |
          PR_NUMBER=$(gh pr list --base trunk --head release-preparation --json number --jq '.[0].number')
          if [ "$PR_NUMBER" != "null" ] && [ -n "$PR_NUMBER" ]; then
            echo "pr-exists=true" >> $GITHUB_OUTPUT
          else
            echo "pr-exists=false" >> $GITHUB_OUTPUT
          fi
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Checkout PR branch
        if: steps.check-pr.outputs.pr-exists == 'true'
        run: |
          git checkout release-preparation
      - name: Run scripts
        run: |
          EXPECTED_BASE_VERSION=${EXPECTED_BASE_VERSION} make update_release_version
          make update_selenium_version_matrix
          make update_browser_versions_matrix
        env:
          EXPECTED_BASE_VERSION: ${{ inputs.grid-version }}
      - name: Commit & Push changes
        if: steps.check-pr.outputs.pr-exists == 'true'
        uses: actions-js/push@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          author_email: "selenium-ci@users.noreply.github.com"
          author_name: "Selenium CI Bot"
          message: "[build] Update Selenium Grid ${{ inputs.grid-version }}"
          empty: true
          rebase: true
          branch: "release-preparation"
      - name: Create Pull Request
        if: steps.check-pr.outputs.pr-exists == 'false'
        uses: peter-evans/create-pull-request@main
        with:
          token: ${{ secrets.SELENIUM_CI_TOKEN }}
          commit-message: |
            [build] Update Selenium Grid ${{ inputs.grid-version }}
          title: "[build] Update Selenium Grid ${{ inputs.grid-version }}"
          body: "This PR to update Selenium Grid ${{ inputs.grid-version }} and backward browser versions"
          committer: 'Selenium CI Bot <selenium-ci@users.noreply.github.com>'
          author: 'Selenium CI Bot <selenium-ci@users.noreply.github.com>'
          branch: release-preparation

================================================
FILE: .github/workflows/rerun-failed.yml
================================================
name: Rerun Workflows

on:
  workflow_dispatch:
    inputs:
      runId:
        description: 'The ID of workflow to rerun'
        required: true
        type: string
      rerunFailedOnly:
        description: 'Rerun only failed jobs'
        required: false
        type: boolean
        default: true

permissions: write-all

env:
  GH_CLI_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  RUN_ID: ${{ github.event.inputs.runId }}
  RERUN_FAILED_ONLY: ${{ github.event.inputs.rerunFailedOnly }}

jobs:
  rerun_workflow:
    name: Rerun ${{ github.event.inputs.runId }}
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
      - name: Install GitHub CLI
        run: |
          sudo apt update
          sudo apt install gh
      - name: Authenticate GitHub CLI
        run: |
          echo "${GH_CLI_TOKEN}" | gh auth login --with-token
      - name: "Rerun workflow ${{ env.RUN_ID }}"
        run: |
          if [ "${RERUN_FAILED_ONLY}" = "true" ]; then
            gh run rerun ${RUN_ID} --failed --repo ${GITHUB_REPOSITORY}
          else
            gh run rerun ${RUN_ID} --repo ${GITHUB_REPOSITORY}
          fi


================================================
FILE: .github/workflows/scan-dockerfile.yml
================================================
name: Scan Dockerfile vulnerabilities
concurrency:
  group: ${{ github.workflow }}

on:
  push:
    paths:
      - '**/Dockerfile'
  pull_request:
    paths:
      - '**/Dockerfile'
  workflow_dispatch:
  schedule:
    - cron: '0 0 * * *'

jobs:
  build-and-scan:
    name: Scan Dockerfile vulnerabilities
    permissions: write-all
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@main
      - name: Set severity for PRs
        if: github.event_name == 'pull_request' || github.event_name == 'push'
        run: |
          echo "SEVERITY=HIGH,CRITICAL" >> $GITHUB_ENV
          echo "EXIT_CODE=1" >> $GITHUB_ENV
      - name: Set severity for others
        if: github.event_name != 'pull_request' && github.event_name != 'push'
        run: |
          echo "SEVERITY=LOW,MEDIUM,HIGH,CRITICAL" >> $GITHUB_ENV
          echo "EXIT_CODE=0" >> $GITHUB_ENV
      - name: Scan source code
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'source-results.sarif'
          scanners: 'vuln,secret,misconfig'
          skip-dirs: 'tests,Video'
          exit-code: '${{ env.EXIT_CODE }}'
          severity: '${{ env.SEVERITY }}'
          limit-severities-for-sarif: true
      - name: Upload source scan results to annotations
        if: always()
        uses: Ayrx/sarif_to_github_annotations@master
        with:
          sarif_file: 'source-results.sarif'
      - name: Upload source scan results to GitHub Security tab
        if: github.event_name != 'pull_request'
        uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: 'source-results.sarif'
          category: source-results


================================================
FILE: .github/workflows/update-chart-readme.yml
================================================
name: Update chart configuration table

on:
  push:
    branches:
      - 'renovate/*'
  pull_request:
    types:
      - opened
    branches:
      - 'renovate/**'
  workflow_dispatch:

jobs:
  update-chart-readme:
    runs-on: ubuntu-24.04
    permissions: write-all
    steps:
      - name: Checkout code
        uses: actions/checkout@main
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Set up environment
        uses: nick-invision/retry@master
        with:
          timeout_minutes: 10
          max_attempts: 3
          command: |
            make setup_dev_env
      - name: Update chart configuration table
        run: make generate_readme_charts
      - name: Check for differences
        id: check_diff
        run: |
          if git diff --exit-code; then
            echo "diff=false" >> $GITHUB_ENV
          else
            echo "diff=true" >> $GITHUB_ENV
          fi
      - name: Commit files
        if: env.diff == 'true'
        run: |
          git config --local user.email "selenium-ci@users.noreply.github.com"
          git config --local user.name "Selenium CI Bot"
          git commit -m "[ci] Update chart configuration table" -m "[skip test]" -a
          git pull --rebase
      - name: Push changes
        if: env.diff == 'true'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.SELENIUM_CI_TOKEN }}
          branch: ${{ env.BRANCH_NAME }}
        env:
          BRANCH_NAME: ${{ github.head_ref || github.ref_name }}


================================================
FILE: .github/workflows/update-dev-beta-browser-images.yml
================================================
name: Update Dev/Beta Browser Images

on:
  workflow_dispatch:
    inputs:
      rerunFailedOnly:
        description: 'Rerun only failed jobs'
        required: false
        type: boolean
        default: true
  schedule:
    # Trigger build every 2 days
    - cron: '0 2 */2 * *'

permissions: write-all

env:
  GH_CLI_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  GH_CLI_TOKEN_PR: ${{ secrets.TRIGGER_CI_TOKEN || secrets.GITHUB_TOKEN }}
  RUN_ID: ${{ github.run_id }}
  RERUN_FAILED_ONLY: ${{ github.event.inputs.rerunFailedOnly || true }}
  RUN_ATTEMPT: ${{ github.run_attempt }}

jobs:

  deploy:

    runs-on: ubuntu-24.04
    strategy: 
      fail-fast: false
      matrix:
        include:
          - browser: chrome
            channel: dev
            platforms: linux/amd64
          - browser: chrome
            channel: beta
            platforms: linux/amd64
          - browser: firefox
            channel: dev
            platforms: linux/amd64,linux/arm64
          - browser: firefox
            channel: beta
            platforms: linux/amd64,linux/arm64
          - browser: edge
            channel: dev
            platforms: linux/amd64
          - browser: edge
            channel: beta
            platforms: linux/amd64
          - browser: chrome-for-testing
            channel: dev
            platforms: linux/amd64
          - browser: chrome-for-testing
            channel: beta
            platforms: linux/amd64
          - browser: chrome-for-testing
            channel: canary
            platforms: linux/amd64
    env:
      NAME: ${{ vars.DOCKER_NAMESPACE || 'selenium' }}
      BROWSER: ${{ matrix.browser }}
      CHANNEL: ${{ matrix.channel }}
      PLATFORMS: ${{ matrix.platforms }}

    steps:
    - name: Checkout code
      uses: actions/checkout@main
    - name: Set up containerd image store feature
      uses: nick-invision/retry@master
      with:
        timeout_minutes: 10
        max_attempts: 3
        command: |
          make setup_dev_env
    - name: Setup environment variables
      run: |
        export SELENIUM_VERSION=$(grep BASE_VERSION Makefile | sed 's/.*,\([^)]*\))/\1/p' | head -n 1)
        export BINDING_VERSION=$(grep BINDING_VERSION Makefile | sed 's/.*,\([^)]*\))/\1/p' | head -n 1)
        echo "SELENIUM_VERSION="$SELENIUM_VERSION >> $GITHUB_ENV
        echo "BINDING_VERSION="$BINDING_VERSION >> $GITHUB_ENV
        export BUILD_DATE=$(date '+%Y%m%d')
        echo "BUILD_DATE="$BUILD_DATE >> $GITHUB_ENV
        export BROWSER_CAPS=`node -p "process.argv[1][0].toUpperCase() + process.argv[1].toString().substring(1)" $BROWSER`
        echo "BROWSER_CAPS="$BROWSER_CAPS >> $GITHUB_ENV
        export CHANNEL_CAPS=`node -p "process.argv[1][0].toUpperCase() + process.argv[1].toString().substring(1)" $CHANNEL`
        echo "CHANNEL_CAPS="$CHANNEL_CAPS >> $GITHUB_ENV
        echo "BROWSER is $BROWSER"
        echo "CHANNEL is $CHANNEL"
        echo "SELENIUM_VERSION is $SELENIUM_VERSION"
        echo "BUILD_DATE is $BUILD_DATE"

    - name: Pull hub and node-base and tag them for faster building and testing
      run: |
        docker pull $NAME/hub:latest
        docker pull $NAME/node-base:latest
        docker tag $NAME/hub:latest $NAME/hub:$CHANNEL
        docker tag $NAME/node-base:latest $NAME/node-base:$CHANNEL
        docker tag $NAME/hub:latest $NAME/hub:"$SELENIUM_VERSION"-"$BUILD_DATE"
        docker tag $NAME/node-base:latest $NAME/node-base:"$SELENIUM_VERSION"-"$BUILD_DATE"
        
    - name: Build the Dev/Beta Docker container images
      run: |
        echo VERSION=$SELENIUM_VERSION PLATFORMS=$PLATFORMS make "$BROWSER"_"$CHANNEL" standalone_"$BROWSER"_"$CHANNEL"
        VERSION=$SELENIUM_VERSION PLATFORMS=$PLATFORMS make "$BROWSER"_"$CHANNEL" standalone_"$BROWSER"_"$CHANNEL"        

    - name: Test the Dev/Beta Docker container images
      run: |
        export SKIP_BUILD=true
        export NAMESPACE=$NAME
        VERSION=$CHANNEL ./tests/bootstrap.sh Node$BROWSER_CAPS
        VERSION=$CHANNEL ./tests/bootstrap.sh Standalone$BROWSER_CAPS

    - name: Login Docker Hub
      run: docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
      env:
        DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
        DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}  

    - name: Deploy Dev/Beta Docker container images  
      run: |
        docker push $NAME/node-$BROWSER:$CHANNEL
        docker push $NAME/standalone-$BROWSER:$CHANNEL
        if [ "$BROWSER" = "firefox" ]; then
          export DRIVER_VERSION_COMMAND="/usr/bin/geckodriver --version | head -n 1"
          export BROWSER_VERSION_COMMAND="firefox -version"
        elif [ "$BROWSER" = "chrome" ]; then
          export DRIVER_VERSION_COMMAND="/usr/bin/chromedriver -version"
          export BROWSER_VERSION_COMMAND="google-chrome -version"
        elif [ "$BROWSER" = "edge" ]; then
          export DRIVER_VERSION_COMMAND="/usr/bin/msedgedriver -version"
          export BROWSER_VERSION_COMMAND="microsoft-edge -version"
        fi
        echo "Push to Docker Hub completed"
        echo "$BROWSER_CAPS $CHANNEL_CAPS browser version:"
        docker run --rm $NAME/standalone-$BROWSER:$CHANNEL bash -c "$BROWSER_VERSION_COMMAND"
        echo "$BROWSER_CAPS $CHANNEL_CAPS WebDriver version:"
        docker run --rm $NAME/standalone-$BROWSER:$CHANNEL bash -c "$DRIVER_VERSION_COMMAND"


  rerun-workflow-when-failure:
    name: Rerun workflow when failure
    needs:
      - deploy
    if: failure() && ( github.run_attempt < 3 )
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout code
        uses: actions/checkout@main
      - name: Install GitHub CLI
        run: |
          sudo apt update
          sudo apt install gh
      - name: Authenticate GitHub CLI for PR
        if: github.event_name == 'pull_request'
        run: |
          echo "$GH_CLI_TOKEN_PR" | gh auth login --with-token
      - name: Authenticate GitHub CLI
        if: github.event_name != 'pull_request'
        run: |
          echo "$GH_CLI_TOKEN" | gh auth login --with-token
      - name: Rerun workflow when failure
        run: |
          echo "Rerun workflow ID $RUN_ID in attempt #$(($RUN_ATTEMPT + 1))"
          gh workflow run rerun-failed.yml \
            --repo $GITHUB_REPOSITORY \
            --raw-field runId=$RUN_ID \
            --raw-field rerunFailedOnly=$RERUN_FAILED_ONLY


================================================
FILE: .gitignore
================================================
tmp/
*_image/
node_modules/
.idea/
*.iml
# Sed backup files.
*-e
# Vim swap files.
*.swp
# Selenium Server Dev
selenium_server_deploy.jar
# assets directory
assets
.vscode

# Ignoring generated files during the build process
StandaloneC*/selenium.conf
StandaloneF*/selenium.conf
StandaloneE*/selenium.conf
StandaloneC*/start-*.sh
StandaloneF*/start-*.sh
StandaloneE*/start-*.sh
StandaloneC*/generate_config
StandaloneF*/generate_config
StandaloneE*/generate_config
videos
Base/configs

# Created by https://www.gitignore.io/api/virtualenv

### VirtualEnv ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
.Python
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
.venv
pip-selfcheck.json

# End of https://www.gitignore.io/api/virtualenv
tests/tests/*
tests/target/*

# Created by https://www.gitignore.io/api/python

### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# End of https://www.gitignore.io/api/python
.DS_Store
/charts/*/charts
/charts/*/**.lock
/charts/*.tgz
/charts/*/RELEASE_NOTES.md
Base/certs


================================================
FILE: .gitpod.yml
================================================
tasks:
  - name: Show README link and build instructions
    before: | 
      echo "Try out Selenium Grid in Docker by following https://github.com/SeleniumHQ/docker-selenium#execution-modes"
      echo "To build all images, run 'make all'. See more instructions at https://github.com/SeleniumHQ/docker-selenium/wiki/Building-your-own-images"



================================================
FILE: .keda/README.md
================================================
# Introduction

Selenium Grid Scaler is a built-in scaler is maintained in upstream KEDA [repository](https://github.com/kedacore/keda). The scaler implementation could be found [here](https://github.com/kedacore/keda/blob/main/pkg/scalers/selenium_grid_scaler.go). The official docs of the scaler could be seen [here](https://keda.sh/docs/latest/scalers/selenium-grid-scaler/).

Now, [SeleniumHQ/docker-selenium](https://github.com/SeleniumHQ/docker-selenium) involves as the maintainer for the scaler.

In order to deliver and get feedback continuously on any new bug fixes, improvement, or features for the Selenium Grid scaler. We select the latest stable version of KEDA core, patch the scaler implementation then build and deploy KEDA container images following our image tag convention.

The stable implementation will be merged to the upstream KEDA repository frequently and will be available in the next KEDA core release.

# How to use the patched scaler

Replace the image registry and tag of these KEDA components with the patched image tag:

```bash
docker pull ghcr.io/kedacore/keda:2.19.0
docker pull ghcr.io/keda-metrics-apiserver:2.19.0
docker pull ghcr.io/keda-admission-webhooks:2.19.0
```

Besides that, you also can use image tag `latest` or `nightly`.

If you are deploying KEDA core using their official Helm [chart](https://github.com/kedacore/charts), you can overwrite the image registry and tag by providing the following values in the `values.yaml` file. For example:

```yaml
  image:
    keda:
      registry: ghcr.io
      repository: keda
      tag: "2.19.0"
    metricsApiServer:
      registry: ghcr.io
      repository: keda-metrics-apiserver
      tag: "2.19.0"
    webhooks:
      registry: ghcr.io
      repository: keda-admission-webhooks
      tag: "2.19.0"
```

If you are deployment Selenium Grid chart with `autoscaling.enabled` is `true` (implies installing KEDA sub-chart), KEDA images registry and tag already set in the `values.yaml`. Refer to list [configuration](../charts/selenium-grid/CONFIGURATION.md).

If you want to disable default patched KEDA image tags in Selenium Grid chart, you can set via Helm CLI `--set keda.image=null` or the same in values file.

# Pull requests under testing

Here is list of pull requests that are under testing and will be merged to the upstream KEDA repository.
You can involve to review and discuss the pull requests to help us early detect and fix any issues.

[kedacore/keda](https://github.com/kedacore/keda)

- ~~https://github.com/kedacore/keda/pull/6772 (merged, v2.17.1)~~

- ~~https://github.com/kedacore/keda/pull/6684 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda/pull/6570 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda/pull/6536 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda/pull/6477 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda/pull/6437 (merged, v2.16.1)~~

- ~~https://github.com/kedacore/keda/pull/6368 (merged, v2.16.1)~~

- ~~https://github.com/kedacore/keda/pull/6169 (merged, v2.16.0)~~

[kedacore/keda-docs](https://github.com/kedacore/keda-docs)

- ~~https://github.com/kedacore/keda-docs/pull/1560 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda-docs/pull/1542 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda-docs/pull/1533 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda-docs/pull/1522 (merged, v2.17.0)~~

- ~~https://github.com/kedacore/keda-docs/pull/1515 (merged, v2.16.1)~~

- ~~https://github.com/kedacore/keda-docs/pull/1468 (merged, v2.16.0)~~

# Test results of the patch scaler

There are tests for the patched scaler implementation. You can run the tests by following the steps in [../tests/README.md](../tests/README.md).

Test results could be referred to

- [results_test_k8s_autoscaling_job_count_strategy_default.md](./results_test_k8s_autoscaling_job_count_strategy_default.md)
- [results_test_k8s_autoscaling_job_count_strategy_default_in_chaos.md](./results_test_k8s_autoscaling_job_count_strategy_default_in_chaos.md)
- [results_test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions.md](./results_test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions.md)
- [results_test_k8s_autoscaling_deployment_count.md](./results_test_k8s_autoscaling_deployment_count.md)
- [results_test_k8s_autoscaling_deployment_count_in_chaos.md](./results_test_k8s_autoscaling_deployment_count_in_chaos.md)
- [results_test_k8s_autoscaling_deployment_count_with_node_max_sessions.md](./results_test_k8s_autoscaling_deployment_count_with_node_max_sessions.md)

# Resources

You can inspect the implementation of current Selenium Grid scaler:

- [selenium_grid_scaler.go](./scalers/selenium_grid_scaler.go)
- [selenium_grid_scaler_test.go](./scalers/selenium_grid_scaler_test.go)
- [selenium-grid-scaler.md](./scalers/selenium-grid-scaler.md)


================================================
FILE: .keda/results_test_k8s_autoscaling_deployment_count.md
================================================
| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total running sessions | Total running pods | Max sessions per pod | Gaps | Sessions closed |
| --------- | -------------------- | --------------------- | ------------------------- | ------------------ | ---------------------- | ------------------ | -------------------- | ---- | --------------- |
| 1         | 1                    | 0.09 s                | 1                         | 0                  | 0                      | 0                  | 1                    | 0    | 0               |
| 2         | 3                    | 42.11 s               | 0                         | 4                  | 3                      | 4                  | 1                    | 1    | 0               |
| 3         | 1                    | 38.92 s               | 0                         | 2                  | 4                      | 6                  | 1                    | 2    | 0               |
| 4         | 2                    | 41.55 s               | 0                         | 2                  | 6                      | 8                  | 1                    | 2    | 0               |
| 5         | 1                    | 8.69 s                | 0                         | 1                  | 7                      | 9                  | 1                    | 2    | 0               |
| 6         | 2                    | 38.24 s               | 0                         | 2                  | 9                      | 11                 | 1                    | 2    | 9               |
| 7         | 1                    | 19.52 s               | 0                         | 0                  | 1                      | 11                 | 1                    | 10   | 0               |
| 8         | 2                    | 5.37 s                | 0                         | 0                  | 3                      | 11                 | 1                    | 8    | 0               |
| 9         | 1                    | 5.23 s                | 0                         | 0                  | 4                      | 11                 | 1                    | 7    | 0               |
| 10        | 1                    | 5.04 s                | 0                         | 0                  | 5                      | 11                 | 1                    | 6    | 0               |
| 11        | 1                    | 15.62 s               | 0                         | 0                  | 6                      | 11                 | 1                    | 5    | 6               |
| 12        | 1                    | 16.90 s               | 0                         | 0                  | 1                      | 11                 | 1                    | 10   | 0               |
| 13        | 3                    | 41.47 s               | 0                         | 2                  | 4                      | 13                 | 1                    | 9    | 0               |
| 14        | 3                    | 11.03 s               | 0                         | -2                 | 7                      | 11                 | 1                    | 4    | 0               |
| 15        | 3                    | 20.94 s               | 0                         | 1                  | 10                     | 12                 | 1                    | 2    | 0               |
| 16        | 2                    | 800.08 s              | 1                         | 1                  | 11                     | 13                 | 1                    | 2    | 11              |
| 17        | 1                    | 4.47 s                | 0                         | 0                  | 1                      | 13                 | 1                    | 12   | 0               |
| 18        | 1                    | 4.10 s                | 0                         | 0                  | 2                      | 13                 | 1                    | 11   | 0               |
| 19        | 2                    | 21.38 s               | 0                         | 0                  | 4                      | 13                 | 1                    | 9    | 0               |
| 20        | 2                    | 15.39 s               | 0                         | 0                  | 6                      | 13                 | 1                    | 7    | 0               |

================================================
FILE: .keda/results_test_k8s_autoscaling_deployment_count_in_chaos.md
================================================
| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total running sessions | Total running pods | Max sessions per pod | Gaps | Sessions closed |
| --------- | -------------------- | --------------------- | ------------------------- | ------------------ | ---------------------- | ------------------ | -------------------- | ---- | --------------- |
| 1         | 4                    | 0.13 s                | 4                         | 0                  | 0                      | 0                  | 1                    | 0    | 0               |
| 2         | 6                    | 73.65 s               | 0                         | 8                  | 6                      | 8                  | 1                    | 2    | 6               |
| 3         | 3                    | 37.36 s               | 0                         | 1                  | 3                      | 9                  | 1                    | 6    | 3               |
| 4         | 4                    | 17.15 s               | 0                         | 0                  | 4                      | 9                  | 1                    | 5    | 4               |
| 5         | 5                    | 41.70 s               | 0                         | 2                  | 5                      | 11                 | 1                    | 6    | 5               |
| 6         | 5                    | 17.69 s               | 0                         | 0                  | 5                      | 11                 | 1                    | 6    | 5               |
| 7         | 4                    | 5.76 s                | 0                         | 0                  | 4                      | 11                 | 1                    | 7    | 3               |
| 8         | 6                    | 29.54 s               | 0                         | 1                  | 7                      | 12                 | 1                    | 5    | 7               |
| 9         | 5                    | 16.25 s               | 0                         | 0                  | 5                      | 11                 | 1                    | 6    | 5               |
| 10        | 3                    | 4.80 s                | 0                         | 0                  | 3                      | 11                 | 1                    | 8    | 3               |
| 11        | 4                    | 5.99 s                | 0                         | 0                  | 4                      | 11                 | 1                    | 7    | 4               |
| 12        | 6                    | 18.34 s               | 0                         | 1                  | 6                      | 11                 | 1                    | 5    | 4               |
| 13        | 5                    | 16.50 s               | 0                         | 0                  | 7                      | 11                 | 1                    | 4    | 7               |
| 14        | 5                    | 17.23 s               | 0                         | 1                  | 5                      | 12                 | 1                    | 7    | 5               |
| 15        | 6                    | 23.02 s               | 0                         | 1                  | 6                      | 13                 | 1                    | 7    | 4               |
| 16        | 5                    | 22.00 s               | 0                         | 0                  | 7                      | 13                 | 1                    | 6    | 6               |
| 17        | 4                    | 16.72 s               | 0                         | 0                  | 5                      | 12                 | 1                    | 7    | 5               |
| 18        | 3                    | 6.07 s                | 0                         | 0                  | 3                      | 12                 | 1                    | 9    | 3               |
| 19        | 6                    | 19.04 s               | 0                         | -1                 | 6                      | 11                 | 1                    | 5    | 6               |
| 20        | 5                    | 36.78 s               | 0                         | 0                  | 5                      | 11                 | 1                    | 6    | 5               |

================================================
FILE: .keda/results_test_k8s_autoscaling_deployment_count_with_node_max_sessions.md
================================================
| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total running sessions | Total running pods | Max sessions per pod | Gaps | Sessions closed |
| --------- | -------------------- | --------------------- | ------------------------- | ------------------ | ---------------------- | ------------------ | -------------------- | ---- | --------------- |
| 1         | 1                    | 0.10 s                | 1                         | 0                  | 0                      | 0                  | 3                    | 0    | 0               |
| 2         | 3                    | 46.81 s               | 0                         | 3                  | 3                      | 3                  | 3                    | 6    | 0               |
| 3         | 3                    | 47.21 s               | 0                         | 5                  | 6                      | 8                  | 3                    | 18   | 0               |
| 4         | 2                    | 11.20 s               | 0                         | 0                  | 8                      | 8                  | 3                    | 16   | 0               |
| 5         | 3                    | 23.23 s               | 0                         | 3                  | 11                     | 13                 | 3                    | 28   | 0               |
| 6         | 1                    | 28.59 s               | 0                         | 0                  | 12                     | 13                 | 3                    | 27   | 12              |
| 7         | 2                    | 24.15 s               | 0                         | 0                  | 2                      | 13                 | 3                    | 37   | 0               |
| 8         | 1                    | 22.19 s               | 0                         | 0                  | 3                      | 13                 | 3                    | 36   | 0               |
| 9         | 1                    | 28.08 s               | 0                         | 0                  | 4                      | 13                 | 3                    | 35   | 0               |
| 10        | 3                    | 26.86 s               | 0                         | 0                  | 7                      | 13                 | 3                    | 32   | 0               |
| 11        | 3                    | 20.52 s               | 0                         | 0                  | 10                     | 13                 | 3                    | 29   | 10              |
| 12        | 3                    | 21.40 s               | 0                         | -1                 | 3                      | 12                 | 3                    | 33   | 0               |
| 13        | 1                    | 26.93 s               | 0                         | 0                  | 4                      | 12                 | 3                    | 32   | 0               |
| 14        | 3                    | 23.53 s               | 0                         | 0                  | 7                      | 12                 | 3                    | 29   | 0               |
| 15        | 1                    | 16.91 s               | 0                         | 0                  | 8                      | 12                 | 3                    | 28   | 0               |
| 16        | 1                    | 6.22 s                | 0                         | 0                  | 9                      | 12                 | 3                    | 27   | 9               |
| 17        | 1                    | 5.18 s                | 0                         | 0                  | 1                      | 12                 | 3                    | 35   | 0               |
| 18        | 2                    | 15.91 s               | 0                         | 0                  | 3                      | 12                 | 3                    | 33   | 0               |
| 19        | 2                    | 22.93 s               | 0                         | -1                 | 5                      | 11                 | 3                    | 28   | 0               |
| 20        | 2                    | 27.37 s               | 0                         | 0                  | 7                      | 11                 | 3                    | 26   | 0               |

================================================
FILE: .keda/results_test_k8s_autoscaling_job_count_strategy_default.md
================================================
| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total running sessions | Total running pods | Max sessions per pod | Gaps | Sessions closed |
| --------- | -------------------- | --------------------- | ------------------------- | ------------------ | ---------------------- | ------------------ | -------------------- | ---- | --------------- |
| 1         | 2                    | 0.11 s                | 2                         | 0                  | 0                      | 0                  | 1                    | 0    | 0               |
| 2         | 1                    | 50.13 s               | 0                         | 1                  | 1                      | 1                  | 1                    | 0    | 0               |
| 3         | 3                    | 44.66 s               | 0                         | 3                  | 4                      | 4                  | 1                    | 0    | 0               |
| 4         | 2                    | 36.88 s               | 0                         | 2                  | 6                      | 6                  | 1                    | 0    | 0               |
| 5         | 3                    | 54.64 s               | 0                         | 3                  | 9                      | 9                  | 1                    | 0    | 0               |
| 6         | 2                    | 44.82 s               | 0                         | 2                  | 11                     | 11                 | 1                    | 0    | 11              |
| 7         | 2                    | 47.65 s               | 0                         | 2                  | 2                      | 2                  | 1                    | 0    | 0               |
| 8         | 3                    | 42.17 s               | 0                         | 3                  | 5                      | 5                  | 1                    | 0    | 0               |
| 9         | 2                    | 44.75 s               | 0                         | 2                  | 7                      | 7                  | 1                    | 0    | 0               |
| 10        | 3                    | 36.41 s               | 0                         | 3                  | 10                     | 10                 | 1                    | 0    | 0               |
| 11        | 3                    | 45.50 s               | 0                         | 3                  | 13                     | 13                 | 1                    | 0    | 13              |
| 12        | 1                    | 46.42 s               | 0                         | 1                  | 1                      | 1                  | 1                    | 0    | 0               |
| 13        | 3                    | 58.77 s               | 0                         | 3                  | 4                      | 4                  | 1                    | 0    | 0               |
| 14        | 3                    | 45.25 s               | 0                         | 3                  | 7                      | 7                  | 1                    | 0    | 0               |
| 15        | 2                    | 44.89 s               | 0                         | 2                  | 9                      | 9                  | 1                    | 0    | 0               |
| 16        | 2                    | 33.60 s               | 0                         | 2                  | 11                     | 11                 | 1                    | 0    | 11              |
| 17        | 2                    | 60.18 s               | 0                         | 2                  | 2                      | 2                  | 1                    | 0    | 0               |
| 18        | 1                    | 29.51 s               | 0                         | 1                  | 3                      | 3                  | 1                    | 0    | 0               |
| 19        | 1                    | 45.14 s               | 0                         | 1                  | 4                      | 4                  | 1                    | 0    | 0               |
| 20        | 3                    | 59.87 s               | 0                         | 3                  | 7                      | 7                  | 1                    | 0    | 0               |

================================================
FILE: .keda/results_test_k8s_autoscaling_job_count_strategy_default_in_chaos.md
================================================
| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total running sessions | Total running pods | Max sessions per pod | Gaps | Sessions closed |
| --------- | -------------------- | --------------------- | ------------------------- | ------------------ | ---------------------- | ------------------ | -------------------- | ---- | --------------- |
| 1         | 3                    | 0.12 s                | 3                         | 0                  | 0                      | 0                  | 1                    | 0    | 0               |
| 2         | 3                    | 37.39 s               | 0                         | 3                  | 3                      | 3                  | 1                    | 0    | 3               |
| 3         | 4                    | 53.51 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 4         | 6                    | 60.61 s               | 0                         | 6                  | 6                      | 6                  | 1                    | 0    | 6               |
| 5         | 6                    | 47.99 s               | 0                         | 6                  | 6                      | 6                  | 1                    | 0    | 6               |
| 6         | 6                    | 50.42 s               | 0                         | 6                  | 6                      | 6                  | 1                    | 0    | 3               |
| 7         | 3                    | 39.59 s               | 0                         | 3                  | 6                      | 6                  | 1                    | 0    | 6               |
| 8         | 4                    | 48.08 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 9         | 3                    | 50.00 s               | 0                         | 3                  | 3                      | 3                  | 1                    | 0    | 3               |
| 10        | 4                    | 41.29 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 11        | 4                    | 44.75 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 12        | 4                    | 51.30 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 13        | 4                    | 58.21 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 14        | 3                    | 51.86 s               | 0                         | 3                  | 3                      | 3                  | 1                    | 0    | 3               |
| 15        | 5                    | 60.19 s               | 0                         | 5                  | 5                      | 5                  | 1                    | 0    | 4               |
| 16        | 4                    | 59.07 s               | 0                         | 4                  | 5                      | 5                  | 1                    | 0    | 5               |
| 17        | 4                    | 57.85 s               | 0                         | 4                  | 4                      | 4                  | 1                    | 0    | 4               |
| 18        | 6                    | 57.36 s               | 0                         | 6                  | 6                      | 6                  | 1                    | 0    | 6               |
| 19        | 6                    | 51.80 s               | 0                         | 6                  | 6                      | 6                  | 1                    | 0    | 5               |
| 20        | 6                    | 51.24 s               | 0                         | 6                  | 7                      | 7                  | 1                    | 0    | 7               |

================================================
FILE: .keda/results_test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions.md
================================================
| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total running sessions | Total running pods | Max sessions per pod | Gaps | Sessions closed |
| --------- | -------------------- | --------------------- | ------------------------- | ------------------ | ---------------------- | ------------------ | -------------------- | ---- | --------------- |
| 1         | 2                    | 0.15 s                | 2                         | 0                  | 0                      | 0                  | 3                    | 0    | 0               |
| 2         | 3                    | 66.47 s               | 0                         | 1                  | 3                      | 1                  | 3                    | 0    | 0               |
| 3         | 3                    | 46.40 s               | 0                         | 3                  | 6                      | 6                  | 3                    | 12   | 0               |
| 4         | 2                    | 30.37 s               | 0                         | 2                  | 8                      | 8                  | 3                    | 16   | 0               |
| 5         | 3                    | 51.06 s               | 0                         | 2                  | 11                     | 10                 | 3                    | 19   | 0               |
| 6         | 2                    | 39.31 s               | 0                         | 2                  | 13                     | 13                 | 3                    | 26   | 13              |
| 7         | 3                    | 47.29 s               | 0                         | 2                  | 3                      | 11                 | 3                    | 30   | 0               |
| 8         | 3                    | 10.26 s               | 0                         | 0                  | 6                      | 11                 | 3                    | 27   | 0               |
| 9         | 2                    | 21.23 s               | 0                         | 0                  | 8                      | 13                 | 3                    | 31   | 0               |
| 10        | 1                    | 5.77 s                | 0                         | 0                  | 9                      | 13                 | 3                    | 30   | 0               |
| 11        | 2                    | 6.57 s                | 0                         | 0                  | 11                     | 13                 | 3                    | 28   | 11              |
| 12        | 1                    | 20.68 s               | 0                         | 0                  | 1                      | 11                 | 3                    | 32   | 0               |
| 13        | 2                    | 11.32 s               | 0                         | 0                  | 3                      | 11                 | 3                    | 30   | 0               |
| 14        | 1                    | 6.01 s                | 0                         | 0                  | 4                      | 11                 | 3                    | 29   | 0               |
| 15        | 1                    | 17.36 s               | 0                         | 0                  | 5                      | 11                 | 3                    | 28   | 0               |
| 16        | 3                    | 33.29 s               | 0                         | 0                  | 8                      | 11                 | 3                    | 25   | 8               |
| 17        | 3                    | 20.68 s               | 0                         | 0                  | 3                      | 9                  | 3                    | 24   | 0               |
| 18        | 1                    | 9.10 s                | 0                         | 0                  | 4                      | 9                  | 3                    | 23   | 0               |
| 19        | 3                    | 18.31 s               | 0                         | 2                  | 7                      | 11                 | 3                    | 26   | 0               |
| 20        | 2                    | 30.45 s               | 0                         | 1                  | 9                      | 12                 | 3                    | 27   | 0               |

================================================
FILE: .keda/scalers/selenium-grid-scaler.md
================================================
+++
title = "Selenium Grid Scaler"
availability = "v2.4+"
maintainer = "Volvo Cars, SeleniumHQ"
category = "Testing"
description = "Scales Selenium browser nodes based on number of requests waiting in session queue"
go_file = "selenium_grid_scaler"
+++

### Trigger Specification

This specification describes the `selenium-grid` trigger that scales browser nodes based on number of requests in session queue and the max sessions per grid.

The scaler creates one browser node per pending request in session queue, divided by the max amount of sessions that can run in parallel. You will have to create one trigger per browser capability that you would like to support in your Selenium Grid.

The below is an example trigger configuration with default values represent.

```yaml
triggers:
  - type: selenium-grid
    metadata:
      url: 'http://selenium-hub:4444/graphql' # Required. Can be ommitted if specified via TriggerAuthentication/ClusterTriggerAuthentication.
      browserName: ''  # Optional. Required to be matched with the request in queue and Node stereotypes (Similarly for `browserVersion` and `platformName`).
      browserVersion: '' # Optional.
      platformName: '' # Optional.
      unsafeSsl: false # Optional.
      activationThreshold: 0 # Optional.
      nodeMaxSessions: 1 # Optional.
      enableManagedDownloads: true # Optional.
      capabilities: '' # Optional.
```

**Parameter list:**

- `url` - Graphql url of your Selenium Grid. Refer to the Selenium Grid's documentation [here](https://www.selenium.dev/documentation/en/grid/grid_4/graphql_support/) to for more info. If endpoint requires authentication, you can use `TriggerAuthentication` to provide the credentials instead of embedding in the URL.
- `browserName` - Name of browser that usually gets passed in the browser capability. Refer to the [Selenium Grid's](https://www.selenium.dev/documentation/en/getting_started_with_webdriver/browsers/) and [WebdriverIO's](https://webdriver.io/docs/options/#capabilities) documentation for more info. (Optional)
- `sessionBrowserName` -  Name of the browser when it is an active session, only set if `BrowserName` changes between the queue and the active session. See the Edge example below for further detail. (Optional)
- `browserVersion` - Version of browser that usually gets passed in the browser capability. Refer to the [Selenium Grid's](https://www.selenium.dev/documentation/en/getting_started_with_webdriver/browsers/) and [WebdriverIO's](https://webdriver.io/docs/options/#capabilities) documentation for more info. (Optional)
- `unsafeSsl` - Skip certificate validation when connecting over HTTPS. (Values: `true`, `false`, Default: `false`, Optional)
- `activationThreshold` - Target value for activating the scaler. Learn more about activation [here](./../concepts/scaling-deployments.md#activating-and-scaling-thresholds). (Default: `0`, Optional)
- `platformName` - Name of the browser platform. Refer to the [Selenium Grid's](https://www.selenium.dev/documentation/en/getting_started_with_webdriver/browsers/) and [WebdriverIO's](https://webdriver.io/docs/options/#capabilities) documentation for more info. (Optional)
- `nodeMaxSessions` - Number of maximum sessions that can run in parallel on a Node. Update this parameter align with node config `--max-sessions` (`SE_NODE_MAX_SESSIONS`) to have the correct scaling behavior. (Default: `1`, Optional).
- `enableManagedDownloads`- Set this for Node enabled to auto manage files downloaded for a given session on the Node. When the client requests enabling this feature, it can only be assigned to the Node that also enabled it. Otherwise, the request will wait until it timed out. (Default: `true`, Optional).
- `capabilities` - Add more custom capabilities for matching specific Nodes. It should be in JSON string, see [example](https://www.selenium.dev/documentation/grid/configuration/toml_options/#setting-custom-capabilities-for-matching-specific-nodes) (Optional)

**Trigger Authentication**
- `username` - Username for basic authentication in GraphQL endpoint instead of embedding in the URL. (Optional)
- `password` - Password for basic authentication in GraphQL endpoint instead of embedding in the URL. (Optional)
- `authType` - Type of authentication to be used. This can be set to `Bearer` or `OAuth2` in case Selenium Grid behind an Ingress proxy with other authentication types. (Optional)
- `accessToken` - Access token. This is required when `authType` is set a value. (Optional)

### Example

---
#### Selenium Grid scaler trigger metadata for Chrome browser with `platformNane` and empty `browserVersion`

Here is a full example of scaled object definition using Selenium Grid trigger:

```yaml
kind: Deployment
metadata:
  name: selenium-node-chrome
  labels:
    deploymentName: selenium-node-chrome
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: selenium-node-chrome
        image: selenium/node-chrome:latest
        ports:
        - containerPort: 5555
        env:
        - name: SE_NODE_BROWSER_VERSION
          value: ''
        - name: SE_NODE_PLATFORM_NAME
          value: 'Linux'

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-scaledobject-chrome
  namespace: keda
  labels:
    deploymentName: selenium-node-chrome
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-chrome
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'chrome'
        platformName: 'Linux'
        unsafeSsl: 'true'
```

Noted:
- From `v2.16.1+`, the trigger metadata `browserVersion`, `platformName` is recommended to be set explicitly to have the correct scaling behavior (especially when your Grid includes autoscaling Nodes, non-autoscaling Nodes, relay Nodes, etc.). Besides that, in client binding, it is also recommended to set the `browserVersion`, `platformName` to align with the trigger metadata. Please see below examples for more details.

The above example will create Chrome browser nodes equal to the requests pending in session queue for Chrome browser, which is created from client. For example in Python binding

```python
options = ChromeOptions()
options.set_capability('platformName', 'Linux')
driver = webdriver.Remote(options=options, command_executor=SELENIUM_GRID_URL)
```

With above script, the request is sent to Grid. Via GraphQL response, it looks like

```json
{
  "data": {
    "grid": {
      "sessionCount": 0,
      "maxSession": 0,
      "totalSlots": 0
    },
    "nodesInfo": {
      "nodes": []
    },
    "sessionsInfo": {
      "sessionQueueRequests": [
        "{\"browserName\": \"chrome\", \"platformName\": \"linux\"}"
      ]
    }
  }
}
```

In Node deployment spec, there is environment variable `SE_NODE_BROWSER_VERSION` which can be set to empty. This is used to unset `browserVersion` in Node stereotypes (it is in project [docker-selenium](https://github.com/SeleniumHQ/docker-selenium) setting short browser build number by default), which is expected to match with the request capabilities in queue and scaler trigger metadata.

When the request capabilities match with scaler trigger metadata, the scaler will create a new Node and connect to the Hub. Now the GraphQL response looks like

```json
{
  "data": {
    "grid": {
      "sessionCount": 0,
      "maxSession": 1,
      "totalSlots": 1
    },
    "nodesInfo": {
      "nodes": [
        {
          "id": "UUID-of-Node",
          "status": "UP",
          "sessionCount": 0,
          "maxSession": 1,
          "slotCount": 1,
          "stereotypes": "[{\"slots\": 1, \"stereotype\": {\"browserName\": \"chrome\", \"browserVersion\": \"\", \"platformName\": \"Linux\"}}]",
          "sessions": []
        }
      ]
    },
    "sessionsInfo": {
      "sessionQueueRequests": [
        "{\"browserName\": \"chrome\", \"platformName\": \"linux\"}"
      ]
    }
  }
}
```

Now, the request can be picked up by the Node and the session is created. Session queue will be cleared and the scaler will not create a new Node until the next request comes in.

---
#### Selenium Grid scaler trigger metadata for Chrome browser with `browserVersion` and `platformName`

Moreover, at the same time, you can create one more scaled object for Chrome browser request with specific `browserVersion`. For example

```yaml
kind: Deployment
metadata:
  name: selenium-node-chrome-131
  labels:
    deploymentName: selenium-node-chrome-131
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: selenium-node-chrome
        image: selenium/node-chrome:131.0
        ports:
        - containerPort: 5555
        env:
        - name: SE_NODE_BROWSER_VERSION
          value: '131.0'
        - name: SE_NODE_PLATFORM_NAME
          value: 'Linux'

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-scaledobject-chrome-131
  namespace: keda
  labels:
    deploymentName: selenium-node-chrome-131
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-chrome-131
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'chrome'
        platformName: 'Linux'
        browserVersion: '131.0'
        unsafeSsl: 'true'
```

The request to trigger this scaler should be

```python
options = ChromeOptions()
options.set_capability('platformName', 'Linux')
options.set_capability('browserVersion', '131.0')
driver = webdriver.Remote(options=options, command_executor=SELENIUM_GRID_URL)
```

#### Selenium Grid scaler trigger metadata with Node `enableManagedDownloads`

In image `selenium/node-chrome`, the environment variable `SE_NODE_ENABLE_MANAGED_DOWNLOADS` is used to append the `--enable-managed-downloads` CLI option to the Node. This option is used to enable the Node to auto manage files downloaded for a given session on the Node. The request with enabling this feature can only be assigned to the Node also enabled it, otherwise the request will be waited until request timed out.

```yaml
kind: Deployment
metadata:
  name: selenium-node-chrome
  labels:
    deploymentName: selenium-node-chrome
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: selenium-node-chrome
          image: selenium/node-chrome:132.0
          ports:
          - containerPort: 5555
          env:
          - name: SE_NODE_BROWSER_VERSION
            value: '132.0'
          - name: SE_NODE_PLATFORM_NAME
            value: 'Linux'
          # https://www.selenium.dev/documentation/grid/configuration/cli_options/#node
          - name: SE_NODE_ENABLE_MANAGED_DOWNLOADS
            value: "true"

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-scaledobject-chrome-132
  namespace: keda
  labels:
    deploymentName: selenium-node-chrome-132
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-chrome-132
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'chrome'
        platformName: 'Linux'
        browserVersion: '132.0'
        unsafeSsl: 'true'
        # Scaler trigger param configuration should be aligned with Node stereotype.
        enableManagedDownloads: "true"
```

The request to trigger this scaler should be

```python
options = ChromeOptions()
options.set_capability('platformName', 'Linux')
options.set_capability('browserVersion', '132.0')
# https://www.selenium.dev/documentation/webdriver/drivers/remote_webdriver/#enable-downloads-in-the-grid
options.enable_downloads = True
driver = webdriver.Remote(options=options, command_executor=SELENIUM_GRID_URL)
```

---
#### Selenium Grid scaler trigger metadata with extra `capabilities`

For an advanced use case, you also can set custom capabilities for matching specific Nodes in the scaler trigger metadata. For example

```yaml
kind: Deployment
metadata:
  name: selenium-node-chrome
  labels:
    deploymentName: selenium-node-chrome
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: selenium-node-chrome
          image: selenium/node-chrome:132.0
          ports:
          - containerPort: 5555
          env:
          - name: SE_NODE_BROWSER_VERSION
            value: '132.0'
          - name: SE_NODE_PLATFORM_NAME
            value: 'Linux'
          # Append custom capabilities to Node stereotype. See: https://github.com/SeleniumHQ/docker-selenium?tab=readme-ov-file#node-configuration-options
          - name: SE_NODE_STEREOTYPE_EXTRA
            value: "{\"myApp:version\":\"beta\", \"myApp:publish:\":\"public\"}"

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-scaledobject-chrome-132
  namespace: keda
  labels:
    deploymentName: selenium-node-chrome-132
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-chrome-132
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'chrome'
        platformName: 'Linux'
        browserVersion: '132.0'
        unsafeSsl: 'true'
        # Add custom capabilities for matching specific Nodes in scaler trigger metadata. See: https://github.com/kedacore/keda/pull/6536
        capabilities: "{\"myApp:version\":\"beta\", \"myApp:publish:\":\"public\"}"
```

The request to trigger this scaler should be

```python
options = ChromeOptions()
options.set_capability('platformName', 'Linux')
options.set_capability('browserVersion', '132.0')
# Add custom capabilities for matching specific Nodes in client binding. See: https://www.selenium.dev/documentation/grid/configuration/toml_options/#setting-custom-capabilities-for-matching-specific-nodes
options.set_capability('myApp:version', 'beta')
options.set_capability('myApp:publish', 'public')
driver = webdriver.Remote(options=options, command_executor=SELENIUM_GRID_URL)
```

---
#### Selenium Grid scaler trigger metadata for Firefox browser

```yaml
kind: Deployment
metadata:
  name: selenium-node-firefox
  labels:
    deploymentName: selenium-node-firefox
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: selenium-node-firefox
          image: selenium/node-firefox:latest
          ports:
          - containerPort: 5555
          env:
          - name: SE_NODE_BROWSER_VERSION
            value: ''
          - name: SE_NODE_PLATFORM_NAME
            value: 'Linux'

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-scaledobject-firefox
  namespace: keda
  labels:
    deploymentName: selenium-node-firefox
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-firefox
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'firefox'
        platformName: 'Linux'
        unsafeSsl: 'true'
```

Request to trigger the scaler

```python
options = FirefoxOptions()
options.set_capability('platformName', 'Linux')
driver = webdriver.Remote(options=options, command_executor=SELENIUM_GRID_URL)
```

---
#### Selenium Grid scaler trigger metadata for Edge browser

Similarly, for Edge. Note that for Edge you must set the `sessionBrowserName` to `msedge` inorder for scaling to work properly.

```yaml
kind: Deployment
metadata:
  name: selenium-node-edge
  labels:
    deploymentName: selenium-node-edge
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: selenium-node-edge
          image: selenium/node-edge:latest
          ports:
          - containerPort: 5555
          env:
          - name: SE_NODE_BROWSER_VERSION
            value: ''
          - name: SE_NODE_PLATFORM_NAME
            value: 'Linux'

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-scaledobject-edge
  namespace: keda
  labels:
    deploymentName: selenium-node-edge
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-edge
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'MicrosoftEdge'
        sessionBrowserName: 'msedge'
        platformName: 'Linux'
        unsafeSsl: 'true'
```

Request to trigger the scaler

```python
options = EdgeOptions()
options.set_capability('platformName', 'Linux')
driver = webdriver.Remote(options=options, command_executor=SELENIUM_GRID_URL)
```

---
#### Selenium Grid scaler trigger metadata for Chrome browser with `nodeMaxSessions`

In case you want to scale from 0 (`minReplicaCount: 0`), and browser nodes are configured different `--max-sessions` greater than 1, you can set `nodeMaxSessions` for scaler align with number of slots available per node to have the correct scaling behavior.

```yaml
kind: Deployment
metadata:
  name: selenium-node-chrome
  labels:
    deploymentName: selenium-node-chrome
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: selenium-node-chrome
        image: selenium/node-chrome:latest
        ports:
        - containerPort: 5555
        env:
        - name: SE_NODE_BROWSER_VERSION
          value: ''
        - name: SE_NODE_PLATFORM_NAME
          value: 'Linux'
        - name: SE_NODE_OVERRIDE_MAX_SESSIONS
          value: 'true'
        - name: SE_NODE_MAX_SESSIONS
          value: '4'

---

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-chrome-scaledobject
  namespace: keda
  labels:
    deploymentName: selenium-node-chrome
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-chrome
  triggers:
    - type: selenium-grid
      metadata:
        url: 'http://selenium-hub:4444/graphql'
        browserName: 'chrome'
        platformName: 'Linux'
        nodeMaxSessions: 4
        unsafeSsl: 'true'
```

### Authentication Parameters

It is possible to specify the Graphql url of your Selenium Grid using authentication parameters. This useful if you have enabled Selenium Grid's Basic HTTP Authentication and would like to keep your credentials secure.

- `url` - Graphql url of your Selenium Grid. Refer to the Selenium Grid's documentation [here](https://www.selenium.dev/documentation/en/grid/grid_4/graphql_support/) for more info.
- `username` - Username for basic authentication in GraphQL endpoint instead of embedding in the URL. (Optional)
- `password` - Password for basic authentication in GraphQL endpoint instead of embedding in the URL. (Optional)

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: selenium-grid-secret
  namespace: keda
type: Opaque
data:
  graphql-url: base64 encoded value of GraphQL URL
  graphql-username: base64 encoded value of GraphQL Username
  graphql-password: base64 encoded value of GraphQL Password
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: keda-trigger-auth-selenium-grid-secret
  namespace: keda
spec:
  secretTargetRef:
  - parameter: url
    name: selenium-grid-secret
    key: graphql-url
  - parameter: username
    name: selenium-grid-secret
    key: graphql-username
  - parameter: password
    name: selenium-grid-secret
    key: graphql-password
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: selenium-grid-chrome-scaledobject
  namespace: keda
  labels:
    deploymentName: selenium-node-chrome
spec:
  maxReplicaCount: 8
  scaleTargetRef:
    name: selenium-node-chrome
  triggers:
    - type: selenium-grid
      metadata:
        browserName: 'chrome'
        platformName: 'Linux'
        unsafeSsl: 'true'
      authenticationRef:
        name: keda-trigger-auth-selenium-grid-secret
```


================================================
FILE: .keda/scalers/selenium_grid_scaler.go
================================================
package scalers

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/go-logr/logr"
	v2 "k8s.io/api/autoscaling/v2"
	"k8s.io/metrics/pkg/apis/external_metrics"

	"github.com/kedacore/keda/v2/pkg/scalers/scalersconfig"
	kedautil "github.com/kedacore/keda/v2/pkg/util"
)

type seleniumGridScaler struct {
	metricType v2.MetricTargetType
	metadata   *seleniumGridScalerMetadata
	httpClient *http.Client
	logger     logr.Logger
}

type seleniumGridScalerMetadata struct {
	triggerIndex int

	URL                    string `keda:"name=url,                      order=authParams;triggerMetadata"`
	AuthType               string `keda:"name=authType,                 order=authParams;resolvedEnv, optional"`
	Username               string `keda:"name=username,                 order=authParams;resolvedEnv, optional"`
	Password               string `keda:"name=password,                 order=authParams;resolvedEnv, optional"`
	AccessToken            string `keda:"name=accessToken,              order=authParams;resolvedEnv, optional"`
	BrowserName            string `keda:"name=browserName,              order=triggerMetadata, optional"`
	SessionBrowserName     string `keda:"name=sessionBrowserName,       order=triggerMetadata, optional"`
	BrowserVersion         string `keda:"name=browserVersion,           order=triggerMetadata, optional"`
	PlatformName           string `keda:"name=platformName,             order=triggerMetadata, optional"`
	ActivationThreshold    int64  `keda:"name=activationThreshold,      order=triggerMetadata, optional"`
	UnsafeSsl              bool   `keda:"name=unsafeSsl,                order=triggerMetadata, default=false"`
	NodeMaxSessions        int64  `keda:"name=nodeMaxSessions,          order=triggerMetadata, default=1"`
	EnableManagedDownloads bool   `keda:"name=enableManagedDownloads,   order=triggerMetadata, default=true"`
	Capabilities           string `keda:"name=capabilities,             order=triggerMetadata, optional"`

	TargetValue int64
}

type Platform struct {
	name   string
	family *Platform
}

type SeleniumResponse struct {
	Data Data `json:"data"`
}

type Data struct {
	Grid         Grid         `json:"grid"`
	NodesInfo    NodesInfo    `json:"nodesInfo"`
	SessionsInfo SessionsInfo `json:"sessionsInfo"`
}

type Grid struct {
	SessionCount int64 `json:"sessionCount"`
	MaxSession   int64 `json:"maxSession"`
	TotalSlots   int64 `json:"totalSlots"`
}

type NodesInfo struct {
	Nodes Nodes `json:"nodes"`
}

type SessionsInfo struct {
	SessionQueueRequests []string `json:"sessionQueueRequests"`
}

type Nodes []struct {
	ID           string   `json:"id"`
	Status       string   `json:"status"`
	SessionCount int64    `json:"sessionCount"`
	MaxSession   int64    `json:"maxSession"`
	SlotCount    int64    `json:"slotCount"`
	Stereotypes  string   `json:"stereotypes"`
	Sessions     Sessions `json:"sessions"`
}

type ReservedNodes struct {
	ID         string `json:"id"`
	MaxSession int64  `json:"maxSession"`
	SlotCount  int64  `json:"slotCount"`
}

type Sessions []struct {
	ID           string `json:"id"`
	Capabilities string `json:"capabilities"`
	Slot         Slot   `json:"slot"`
}

type Slot struct {
	ID         string `json:"id"`
	Stereotype string `json:"stereotype"`
}

type Stereotypes []struct {
	Slots      int64                  `json:"slots"`
	Stereotype map[string]interface{} `json:"stereotype"`
}

const EnableManagedDownloadsCapability = "se:downloadsEnabled"

var ExtensionCapabilitiesPrefixes = []string{"goog:", "moz:", "ms:", "se:"}
var FunctionCapabilitiesPrefixes = []string{EnableManagedDownloadsCapability}

// Follow pattern in https://github.com/SeleniumHQ/selenium/blob/trunk/java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java
func filterCapabilities(capabilities map[string]interface{}) map[string]interface{} {
	filteredCapabilities := map[string]interface{}{}

	for key, value := range capabilities {
		retain := true
		for _, excludePrefix := range ExtensionCapabilitiesPrefixes {
			if strings.HasPrefix(key, excludePrefix) {
				retain = false
				break
			}
		}
		for _, prefix := range FunctionCapabilitiesPrefixes {
			if strings.HasPrefix(key, prefix) {
				retain = true
				break
			}
		}
		if retain {
			filteredCapabilities[key] = value
		}
	}

	return filteredCapabilities
}

func NewSeleniumGridScaler(config *scalersconfig.ScalerConfig) (Scaler, error) {
	metricType, err := GetMetricTargetType(config)
	if err != nil {
		return nil, fmt.Errorf("error getting scaler metric type: %w", err)
	}

	logger := InitializeLogger(config, "selenium_grid_scaler")

	meta, err := parseSeleniumGridScalerMetadata(config)

	if err != nil {
		return nil, fmt.Errorf("error parsing selenium grid metadata: %w", err)
	}

	httpClient := kedautil.CreateHTTPClient(config.GlobalHTTPTimeout, meta.UnsafeSsl)

	return &seleniumGridScaler{
		metricType: metricType,
		metadata:   meta,
		httpClient: httpClient,
		logger:     logger,
	}, nil
}

func parseCapabilitiesToMap(_capabilities string) (map[string]interface{}, error) {
	capabilities := map[string]interface{}{}
	if _capabilities != "" {
		if err := json.Unmarshal([]byte(_capabilities), &capabilities); err != nil {
			return nil, err
		}
	}
	return capabilities, nil
}

func parseSeleniumGridScalerMetadata(config *scalersconfig.ScalerConfig) (*seleniumGridScalerMetadata, error) {
	meta := &seleniumGridScalerMetadata{
		TargetValue: 1,
	}

	if err := config.TypedConfig(meta); err != nil {
		return nil, fmt.Errorf("error parsing prometheus metadata: %w", err)
	}

	meta.triggerIndex = config.TriggerIndex

	if meta.SessionBrowserName == "" {
		meta.SessionBrowserName = meta.BrowserName
	}

	return meta, nil
}

// No cleanup required for Selenium Grid scaler
func (s *seleniumGridScaler) Close(context.Context) error {
	if s.httpClient != nil {
		s.httpClient.CloseIdleConnections()
	}
	return nil
}

func (s *seleniumGridScaler) GetMetricsAndActivity(ctx context.Context, metricName string) ([]external_metrics.ExternalMetricValue, bool, error) {
	newRequestNodes, onGoingSessions, err := s.getSessionsQueueLength(ctx, s.logger)
	if err != nil {
		return []external_metrics.ExternalMetricValue{}, false, fmt.Errorf("error requesting selenium grid endpoint: %w", err)
	}

	metric := GenerateMetricInMili(metricName, float64(newRequestNodes+onGoingSessions))

	re
Download .txt
gitextract_11114r06/

├── .circleci/
│   └── config.bak
├── .editorconfig
├── .ffmpeg/
│   └── Dockerfile
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_proposal.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── actions/
│   │   └── get-latest-upstream/
│   │       └── action.yml
│   ├── dependabot.yml
│   ├── label-commenter-config.yml
│   └── workflows/
│       ├── build-ffmpeg.yml
│       ├── build-test.yml
│       ├── create-changelog-pr.yml
│       ├── deploy.yml
│       ├── docker-test.yml
│       ├── helm-chart-release.yml
│       ├── helm-chart-test.yml
│       ├── k8s-scaling-test.yml
│       ├── label-commenter.yml
│       ├── lock.yml
│       ├── nightly.yml
│       ├── release-all-browser-versions.yml
│       ├── release-chrome-for-testing-versions.yml
│       ├── release-chrome-versions.yml
│       ├── release-edge-versions.yml
│       ├── release-firefox-versions.yml
│       ├── release-preparation.yml
│       ├── rerun-failed.yml
│       ├── scan-dockerfile.yml
│       ├── update-chart-readme.yml
│       └── update-dev-beta-browser-images.yml
├── .gitignore
├── .gitpod.yml
├── .keda/
│   ├── README.md
│   ├── results_test_k8s_autoscaling_deployment_count.md
│   ├── results_test_k8s_autoscaling_deployment_count_in_chaos.md
│   ├── results_test_k8s_autoscaling_deployment_count_with_node_max_sessions.md
│   ├── results_test_k8s_autoscaling_job_count_strategy_default.md
│   ├── results_test_k8s_autoscaling_job_count_strategy_default_in_chaos.md
│   ├── results_test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions.md
│   └── scalers/
│       ├── selenium-grid-scaler.md
│       ├── selenium_grid_scaler.go
│       └── selenium_grid_scaler_test.go
├── Base/
│   ├── Dockerfile
│   ├── check-grid.sh
│   ├── entry_point.sh
│   ├── handle_heap_dump.sh
│   ├── mask
│   └── supervisord.conf
├── CHANGELOG/
│   ├── 4.41.0/
│   │   ├── chrome-for-testing_113.md
│   │   ├── chrome-for-testing_114.md
│   │   ├── chrome-for-testing_115.md
│   │   ├── chrome-for-testing_116.md
│   │   ├── chrome-for-testing_117.md
│   │   ├── chrome-for-testing_118.md
│   │   ├── chrome-for-testing_119.md
│   │   ├── chrome-for-testing_120.md
│   │   ├── chrome-for-testing_121.md
│   │   ├── chrome-for-testing_122.md
│   │   ├── chrome-for-testing_123.md
│   │   ├── chrome-for-testing_124.md
│   │   ├── chrome-for-testing_125.md
│   │   ├── chrome-for-testing_126.md
│   │   ├── chrome-for-testing_127.md
│   │   ├── chrome-for-testing_128.md
│   │   ├── chrome-for-testing_129.md
│   │   ├── chrome-for-testing_130.md
│   │   ├── chrome-for-testing_131.md
│   │   ├── chrome-for-testing_132.md
│   │   ├── chrome-for-testing_133.md
│   │   ├── chrome-for-testing_134.md
│   │   ├── chrome-for-testing_135.md
│   │   ├── chrome-for-testing_136.md
│   │   ├── chrome-for-testing_137.md
│   │   ├── chrome-for-testing_138.md
│   │   ├── chrome-for-testing_139.md
│   │   ├── chrome-for-testing_140.md
│   │   ├── chrome-for-testing_141.md
│   │   ├── chrome-for-testing_142.md
│   │   ├── chrome-for-testing_143.md
│   │   ├── chrome-for-testing_144.md
│   │   ├── chrome-for-testing_145.md
│   │   ├── chrome_100.md
│   │   ├── chrome_101.md
│   │   ├── chrome_102.md
│   │   ├── chrome_103.md
│   │   ├── chrome_104.md
│   │   ├── chrome_105.md
│   │   ├── chrome_106.md
│   │   ├── chrome_107.md
│   │   ├── chrome_108.md
│   │   ├── chrome_109.md
│   │   ├── chrome_110.md
│   │   ├── chrome_111.md
│   │   ├── chrome_112.md
│   │   ├── chrome_113.md
│   │   ├── chrome_114.md
│   │   ├── chrome_115.md
│   │   ├── chrome_116.md
│   │   ├── chrome_117.md
│   │   ├── chrome_118.md
│   │   ├── chrome_119.md
│   │   ├── chrome_120.md
│   │   ├── chrome_121.md
│   │   ├── chrome_122.md
│   │   ├── chrome_123.md
│   │   ├── chrome_124.md
│   │   ├── chrome_125.md
│   │   ├── chrome_126.md
│   │   ├── chrome_127.md
│   │   ├── chrome_128.md
│   │   ├── chrome_129.md
│   │   ├── chrome_130.md
│   │   ├── chrome_131.md
│   │   ├── chrome_132.md
│   │   ├── chrome_133.md
│   │   ├── chrome_134.md
│   │   ├── chrome_135.md
│   │   ├── chrome_136.md
│   │   ├── chrome_137.md
│   │   ├── chrome_138.md
│   │   ├── chrome_139.md
│   │   ├── chrome_140.md
│   │   ├── chrome_141.md
│   │   ├── chrome_142.md
│   │   ├── chrome_143.md
│   │   ├── chrome_144.md
│   │   ├── chrome_145.md
│   │   ├── chrome_95.md
│   │   ├── chrome_96.md
│   │   ├── chrome_97.md
│   │   ├── chrome_98.md
│   │   ├── chrome_99.md
│   │   ├── edge_114.md
│   │   ├── edge_115.md
│   │   ├── edge_116.md
│   │   ├── edge_117.md
│   │   ├── edge_118.md
│   │   ├── edge_119.md
│   │   ├── edge_120.md
│   │   ├── edge_121.md
│   │   ├── edge_122.md
│   │   ├── edge_123.md
│   │   ├── edge_124.md
│   │   ├── edge_125.md
│   │   ├── edge_126.md
│   │   ├── edge_127.md
│   │   ├── edge_128.md
│   │   ├── edge_129.md
│   │   ├── edge_130.md
│   │   ├── edge_131.md
│   │   ├── edge_132.md
│   │   ├── edge_133.md
│   │   ├── edge_134.md
│   │   ├── edge_135.md
│   │   ├── edge_136.md
│   │   ├── edge_137.md
│   │   ├── edge_138.md
│   │   ├── edge_139.md
│   │   ├── edge_140.md
│   │   ├── edge_141.md
│   │   ├── edge_142.md
│   │   ├── edge_143.md
│   │   ├── edge_144.md
│   │   ├── edge_145.md
│   │   ├── firefox_100.md
│   │   ├── firefox_101.md
│   │   ├── firefox_102.md
│   │   ├── firefox_103.md
│   │   ├── firefox_104.md
│   │   ├── firefox_105.md
│   │   ├── firefox_106.md
│   │   ├── firefox_107.md
│   │   ├── firefox_108.md
│   │   ├── firefox_109.md
│   │   ├── firefox_110.md
│   │   ├── firefox_111.md
│   │   ├── firefox_112.md
│   │   ├── firefox_113.md
│   │   ├── firefox_114.md
│   │   ├── firefox_115.md
│   │   ├── firefox_116.md
│   │   ├── firefox_117.md
│   │   ├── firefox_118.md
│   │   ├── firefox_119.md
│   │   ├── firefox_120.md
│   │   ├── firefox_121.md
│   │   ├── firefox_122.md
│   │   ├── firefox_123.md
│   │   ├── firefox_124.md
│   │   ├── firefox_125.md
│   │   ├── firefox_126.md
│   │   ├── firefox_127.md
│   │   ├── firefox_128.md
│   │   ├── firefox_129.md
│   │   ├── firefox_130.md
│   │   ├── firefox_131.md
│   │   ├── firefox_132.md
│   │   ├── firefox_133.md
│   │   ├── firefox_134.md
│   │   ├── firefox_135.md
│   │   ├── firefox_136.md
│   │   ├── firefox_137.md
│   │   ├── firefox_138.md
│   │   ├── firefox_139.md
│   │   ├── firefox_140.md
│   │   ├── firefox_141.md
│   │   ├── firefox_142.md
│   │   ├── firefox_143.md
│   │   ├── firefox_144.md
│   │   ├── firefox_145.md
│   │   ├── firefox_146.md
│   │   ├── firefox_147.md
│   │   ├── firefox_148.md
│   │   ├── firefox_98.md
│   │   └── firefox_99.md
│   ├── README.md
│   ├── archived/
│   │   ├── 4.28.1/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.29.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.30.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.31.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.32.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.33.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.34.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.35.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.36.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.37.0/
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_143.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.38.0/
│   │   │   ├── chrome-for-testing_113.md
│   │   │   ├── chrome-for-testing_114.md
│   │   │   ├── chrome-for-testing_115.md
│   │   │   ├── chrome-for-testing_116.md
│   │   │   ├── chrome-for-testing_117.md
│   │   │   ├── chrome-for-testing_118.md
│   │   │   ├── chrome-for-testing_119.md
│   │   │   ├── chrome-for-testing_120.md
│   │   │   ├── chrome-for-testing_121.md
│   │   │   ├── chrome-for-testing_122.md
│   │   │   ├── chrome-for-testing_123.md
│   │   │   ├── chrome-for-testing_124.md
│   │   │   ├── chrome-for-testing_125.md
│   │   │   ├── chrome-for-testing_126.md
│   │   │   ├── chrome-for-testing_127.md
│   │   │   ├── chrome-for-testing_128.md
│   │   │   ├── chrome-for-testing_129.md
│   │   │   ├── chrome-for-testing_130.md
│   │   │   ├── chrome-for-testing_131.md
│   │   │   ├── chrome-for-testing_132.md
│   │   │   ├── chrome-for-testing_133.md
│   │   │   ├── chrome-for-testing_134.md
│   │   │   ├── chrome-for-testing_135.md
│   │   │   ├── chrome-for-testing_136.md
│   │   │   ├── chrome-for-testing_137.md
│   │   │   ├── chrome-for-testing_138.md
│   │   │   ├── chrome-for-testing_139.md
│   │   │   ├── chrome-for-testing_140.md
│   │   │   ├── chrome-for-testing_141.md
│   │   │   ├── chrome-for-testing_142.md
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_141.md
│   │   │   ├── chrome_142.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── edge_141.md
│   │   │   ├── edge_142.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_143.md
│   │   │   ├── firefox_144.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   ├── 4.39.0/
│   │   │   ├── chrome-for-testing_113.md
│   │   │   ├── chrome-for-testing_114.md
│   │   │   ├── chrome-for-testing_115.md
│   │   │   ├── chrome-for-testing_116.md
│   │   │   ├── chrome-for-testing_117.md
│   │   │   ├── chrome-for-testing_118.md
│   │   │   ├── chrome-for-testing_119.md
│   │   │   ├── chrome-for-testing_120.md
│   │   │   ├── chrome-for-testing_121.md
│   │   │   ├── chrome-for-testing_122.md
│   │   │   ├── chrome-for-testing_123.md
│   │   │   ├── chrome-for-testing_124.md
│   │   │   ├── chrome-for-testing_125.md
│   │   │   ├── chrome-for-testing_126.md
│   │   │   ├── chrome-for-testing_127.md
│   │   │   ├── chrome-for-testing_128.md
│   │   │   ├── chrome-for-testing_129.md
│   │   │   ├── chrome-for-testing_130.md
│   │   │   ├── chrome-for-testing_131.md
│   │   │   ├── chrome-for-testing_132.md
│   │   │   ├── chrome-for-testing_133.md
│   │   │   ├── chrome-for-testing_134.md
│   │   │   ├── chrome-for-testing_135.md
│   │   │   ├── chrome-for-testing_136.md
│   │   │   ├── chrome-for-testing_137.md
│   │   │   ├── chrome-for-testing_138.md
│   │   │   ├── chrome-for-testing_139.md
│   │   │   ├── chrome-for-testing_140.md
│   │   │   ├── chrome-for-testing_141.md
│   │   │   ├── chrome-for-testing_142.md
│   │   │   ├── chrome-for-testing_143.md
│   │   │   ├── chrome_100.md
│   │   │   ├── chrome_101.md
│   │   │   ├── chrome_102.md
│   │   │   ├── chrome_103.md
│   │   │   ├── chrome_104.md
│   │   │   ├── chrome_105.md
│   │   │   ├── chrome_106.md
│   │   │   ├── chrome_107.md
│   │   │   ├── chrome_108.md
│   │   │   ├── chrome_109.md
│   │   │   ├── chrome_110.md
│   │   │   ├── chrome_111.md
│   │   │   ├── chrome_112.md
│   │   │   ├── chrome_113.md
│   │   │   ├── chrome_114.md
│   │   │   ├── chrome_115.md
│   │   │   ├── chrome_116.md
│   │   │   ├── chrome_117.md
│   │   │   ├── chrome_118.md
│   │   │   ├── chrome_119.md
│   │   │   ├── chrome_120.md
│   │   │   ├── chrome_121.md
│   │   │   ├── chrome_122.md
│   │   │   ├── chrome_123.md
│   │   │   ├── chrome_124.md
│   │   │   ├── chrome_125.md
│   │   │   ├── chrome_126.md
│   │   │   ├── chrome_127.md
│   │   │   ├── chrome_128.md
│   │   │   ├── chrome_129.md
│   │   │   ├── chrome_130.md
│   │   │   ├── chrome_131.md
│   │   │   ├── chrome_132.md
│   │   │   ├── chrome_133.md
│   │   │   ├── chrome_134.md
│   │   │   ├── chrome_135.md
│   │   │   ├── chrome_136.md
│   │   │   ├── chrome_137.md
│   │   │   ├── chrome_138.md
│   │   │   ├── chrome_139.md
│   │   │   ├── chrome_140.md
│   │   │   ├── chrome_141.md
│   │   │   ├── chrome_142.md
│   │   │   ├── chrome_143.md
│   │   │   ├── chrome_95.md
│   │   │   ├── chrome_96.md
│   │   │   ├── chrome_97.md
│   │   │   ├── chrome_98.md
│   │   │   ├── chrome_99.md
│   │   │   ├── edge_114.md
│   │   │   ├── edge_115.md
│   │   │   ├── edge_116.md
│   │   │   ├── edge_117.md
│   │   │   ├── edge_118.md
│   │   │   ├── edge_119.md
│   │   │   ├── edge_120.md
│   │   │   ├── edge_121.md
│   │   │   ├── edge_122.md
│   │   │   ├── edge_123.md
│   │   │   ├── edge_124.md
│   │   │   ├── edge_125.md
│   │   │   ├── edge_126.md
│   │   │   ├── edge_127.md
│   │   │   ├── edge_128.md
│   │   │   ├── edge_129.md
│   │   │   ├── edge_130.md
│   │   │   ├── edge_131.md
│   │   │   ├── edge_132.md
│   │   │   ├── edge_133.md
│   │   │   ├── edge_134.md
│   │   │   ├── edge_135.md
│   │   │   ├── edge_136.md
│   │   │   ├── edge_137.md
│   │   │   ├── edge_138.md
│   │   │   ├── edge_139.md
│   │   │   ├── edge_140.md
│   │   │   ├── edge_141.md
│   │   │   ├── edge_142.md
│   │   │   ├── edge_143.md
│   │   │   ├── firefox_100.md
│   │   │   ├── firefox_101.md
│   │   │   ├── firefox_102.md
│   │   │   ├── firefox_103.md
│   │   │   ├── firefox_104.md
│   │   │   ├── firefox_105.md
│   │   │   ├── firefox_106.md
│   │   │   ├── firefox_107.md
│   │   │   ├── firefox_108.md
│   │   │   ├── firefox_109.md
│   │   │   ├── firefox_110.md
│   │   │   ├── firefox_111.md
│   │   │   ├── firefox_112.md
│   │   │   ├── firefox_113.md
│   │   │   ├── firefox_114.md
│   │   │   ├── firefox_115.md
│   │   │   ├── firefox_116.md
│   │   │   ├── firefox_117.md
│   │   │   ├── firefox_118.md
│   │   │   ├── firefox_119.md
│   │   │   ├── firefox_120.md
│   │   │   ├── firefox_121.md
│   │   │   ├── firefox_122.md
│   │   │   ├── firefox_123.md
│   │   │   ├── firefox_124.md
│   │   │   ├── firefox_125.md
│   │   │   ├── firefox_126.md
│   │   │   ├── firefox_127.md
│   │   │   ├── firefox_128.md
│   │   │   ├── firefox_129.md
│   │   │   ├── firefox_130.md
│   │   │   ├── firefox_131.md
│   │   │   ├── firefox_132.md
│   │   │   ├── firefox_133.md
│   │   │   ├── firefox_134.md
│   │   │   ├── firefox_135.md
│   │   │   ├── firefox_136.md
│   │   │   ├── firefox_137.md
│   │   │   ├── firefox_138.md
│   │   │   ├── firefox_139.md
│   │   │   ├── firefox_140.md
│   │   │   ├── firefox_141.md
│   │   │   ├── firefox_142.md
│   │   │   ├── firefox_143.md
│   │   │   ├── firefox_144.md
│   │   │   ├── firefox_145.md
│   │   │   ├── firefox_146.md
│   │   │   ├── firefox_98.md
│   │   │   └── firefox_99.md
│   │   └── 4.40.0/
│   │       ├── chrome-for-testing_113.md
│   │       ├── chrome-for-testing_114.md
│   │       ├── chrome-for-testing_115.md
│   │       ├── chrome-for-testing_116.md
│   │       ├── chrome-for-testing_117.md
│   │       ├── chrome-for-testing_118.md
│   │       ├── chrome-for-testing_119.md
│   │       ├── chrome-for-testing_120.md
│   │       ├── chrome-for-testing_121.md
│   │       ├── chrome-for-testing_122.md
│   │       ├── chrome-for-testing_123.md
│   │       ├── chrome-for-testing_124.md
│   │       ├── chrome-for-testing_125.md
│   │       ├── chrome-for-testing_126.md
│   │       ├── chrome-for-testing_127.md
│   │       ├── chrome-for-testing_128.md
│   │       ├── chrome-for-testing_129.md
│   │       ├── chrome-for-testing_130.md
│   │       ├── chrome-for-testing_131.md
│   │       ├── chrome-for-testing_132.md
│   │       ├── chrome-for-testing_133.md
│   │       ├── chrome-for-testing_134.md
│   │       ├── chrome-for-testing_135.md
│   │       ├── chrome-for-testing_136.md
│   │       ├── chrome-for-testing_137.md
│   │       ├── chrome-for-testing_138.md
│   │       ├── chrome-for-testing_139.md
│   │       ├── chrome-for-testing_140.md
│   │       ├── chrome-for-testing_141.md
│   │       ├── chrome-for-testing_142.md
│   │       ├── chrome-for-testing_143.md
│   │       ├── chrome-for-testing_144.md
│   │       ├── chrome-for-testing_145.md
│   │       ├── chrome_100.md
│   │       ├── chrome_101.md
│   │       ├── chrome_102.md
│   │       ├── chrome_103.md
│   │       ├── chrome_104.md
│   │       ├── chrome_105.md
│   │       ├── chrome_106.md
│   │       ├── chrome_107.md
│   │       ├── chrome_108.md
│   │       ├── chrome_109.md
│   │       ├── chrome_110.md
│   │       ├── chrome_111.md
│   │       ├── chrome_112.md
│   │       ├── chrome_113.md
│   │       ├── chrome_114.md
│   │       ├── chrome_115.md
│   │       ├── chrome_116.md
│   │       ├── chrome_117.md
│   │       ├── chrome_118.md
│   │       ├── chrome_119.md
│   │       ├── chrome_120.md
│   │       ├── chrome_121.md
│   │       ├── chrome_122.md
│   │       ├── chrome_123.md
│   │       ├── chrome_124.md
│   │       ├── chrome_125.md
│   │       ├── chrome_126.md
│   │       ├── chrome_127.md
│   │       ├── chrome_128.md
│   │       ├── chrome_129.md
│   │       ├── chrome_130.md
│   │       ├── chrome_131.md
│   │       ├── chrome_132.md
│   │       ├── chrome_133.md
│   │       ├── chrome_134.md
│   │       ├── chrome_135.md
│   │       ├── chrome_136.md
│   │       ├── chrome_137.md
│   │       ├── chrome_138.md
│   │       ├── chrome_139.md
│   │       ├── chrome_140.md
│   │       ├── chrome_141.md
│   │       ├── chrome_142.md
│   │       ├── chrome_143.md
│   │       ├── chrome_144.md
│   │       ├── chrome_95.md
│   │       ├── chrome_96.md
│   │       ├── chrome_97.md
│   │       ├── chrome_98.md
│   │       ├── chrome_99.md
│   │       ├── edge_114.md
│   │       ├── edge_115.md
│   │       ├── edge_116.md
│   │       ├── edge_117.md
│   │       ├── edge_118.md
│   │       ├── edge_119.md
│   │       ├── edge_120.md
│   │       ├── edge_121.md
│   │       ├── edge_122.md
│   │       ├── edge_123.md
│   │       ├── edge_124.md
│   │       ├── edge_125.md
│   │       ├── edge_126.md
│   │       ├── edge_127.md
│   │       ├── edge_128.md
│   │       ├── edge_129.md
│   │       ├── edge_130.md
│   │       ├── edge_131.md
│   │       ├── edge_132.md
│   │       ├── edge_133.md
│   │       ├── edge_134.md
│   │       ├── edge_135.md
│   │       ├── edge_136.md
│   │       ├── edge_137.md
│   │       ├── edge_138.md
│   │       ├── edge_139.md
│   │       ├── edge_140.md
│   │       ├── edge_141.md
│   │       ├── edge_142.md
│   │       ├── edge_143.md
│   │       ├── edge_144.md
│   │       ├── firefox_100.md
│   │       ├── firefox_101.md
│   │       ├── firefox_102.md
│   │       ├── firefox_103.md
│   │       ├── firefox_104.md
│   │       ├── firefox_105.md
│   │       ├── firefox_106.md
│   │       ├── firefox_107.md
│   │       ├── firefox_108.md
│   │       ├── firefox_109.md
│   │       ├── firefox_110.md
│   │       ├── firefox_111.md
│   │       ├── firefox_112.md
│   │       ├── firefox_113.md
│   │       ├── firefox_114.md
│   │       ├── firefox_115.md
│   │       ├── firefox_116.md
│   │       ├── firefox_117.md
│   │       ├── firefox_118.md
│   │       ├── firefox_119.md
│   │       ├── firefox_120.md
│   │       ├── firefox_121.md
│   │       ├── firefox_122.md
│   │       ├── firefox_123.md
│   │       ├── firefox_124.md
│   │       ├── firefox_125.md
│   │       ├── firefox_126.md
│   │       ├── firefox_127.md
│   │       ├── firefox_128.md
│   │       ├── firefox_129.md
│   │       ├── firefox_130.md
│   │       ├── firefox_131.md
│   │       ├── firefox_132.md
│   │       ├── firefox_133.md
│   │       ├── firefox_134.md
│   │       ├── firefox_135.md
│   │       ├── firefox_136.md
│   │       ├── firefox_137.md
│   │       ├── firefox_138.md
│   │       ├── firefox_139.md
│   │       ├── firefox_140.md
│   │       ├── firefox_141.md
│   │       ├── firefox_142.md
│   │       ├── firefox_143.md
│   │       ├── firefox_144.md
│   │       ├── firefox_145.md
│   │       ├── firefox_146.md
│   │       ├── firefox_147.md
│   │       ├── firefox_98.md
│   │       └── firefox_99.md
│   └── generate-matrix-readme.py
├── CONTRIBUTING.md
├── Distributor/
│   ├── Dockerfile
│   ├── selenium-grid-distributor.conf
│   └── start-selenium-grid-distributor.sh
├── ENV_VARIABLES.md
├── EventBus/
│   ├── Dockerfile
│   ├── selenium-grid-eventbus.conf
│   └── start-selenium-grid-eventbus.sh
├── Hub/
│   ├── Dockerfile
│   ├── example-config.toml
│   ├── selenium-grid-hub.conf
│   └── start-selenium-grid-hub.sh
├── LICENSE.md
├── Makefile
├── NodeAllBrowsers/
│   ├── Dockerfile
│   ├── fluxbox-menu-browser-aarch64
│   └── fluxbox-menu-browser-amd64
├── NodeBase/
│   ├── Dockerfile
│   ├── fluxbox-menu
│   ├── generate_config
│   ├── generate_relay_config
│   ├── json_merge.py
│   ├── selenium.conf
│   ├── start-novnc.sh
│   ├── start-selenium-node.sh
│   ├── start-vnc.sh
│   └── start-xvfb.sh
├── NodeChrome/
│   ├── Dockerfile
│   ├── chrome-cleanup.conf
│   ├── chrome-cleanup.sh
│   ├── fluxbox-menu-browser
│   ├── install-chrome-for-testing.sh
│   ├── install-chrome.sh
│   ├── install-chromedriver.sh
│   ├── update-chrome-components.sh
│   └── wrap_chrome_binary
├── NodeChromium/
│   ├── Dockerfile
│   ├── chrome-cleanup.conf
│   ├── chrome-cleanup.sh
│   ├── fluxbox-menu-browser
│   └── wrap_chromium_binary
├── NodeDocker/
│   ├── Dockerfile
│   ├── config.toml
│   ├── selenium-grid-docker.conf
│   ├── start-selenium-grid-docker.sh
│   └── start-socat.sh
├── NodeEdge/
│   ├── Dockerfile
│   ├── edge-cleanup.conf
│   ├── edge-cleanup.sh
│   ├── fluxbox-menu-browser
│   └── wrap_edge_binary
├── NodeFirefox/
│   ├── Dockerfile
│   ├── firefox-cleanup.conf
│   ├── firefox-cleanup.sh
│   ├── fluxbox-menu-browser
│   ├── get_lang_package.sh
│   ├── install-firefox-apt.sh
│   └── install-firefox-package.sh
├── NodeKubernetes/
│   ├── Dockerfile
│   ├── config.toml
│   ├── selenium-grid-kubernetes.conf
│   └── start-selenium-grid-kubernetes.sh
├── README.md
├── Router/
│   ├── Dockerfile
│   ├── selenium-grid-router.conf
│   └── start-selenium-grid-router.sh
├── SessionQueue/
│   ├── Dockerfile
│   ├── selenium-grid-session-queue.conf
│   └── start-selenium-grid-session-queue.sh
├── Sessions/
│   ├── Dockerfile
│   ├── generate_config
│   ├── init.sql
│   ├── selenium-grid-sessions.conf
│   └── start-selenium-grid-sessions.sh
├── Standalone/
│   ├── Dockerfile
│   ├── selenium.conf
│   └── start-selenium-standalone.sh
├── StandaloneDocker/
│   ├── Dockerfile
│   └── start-selenium-grid-docker.sh
├── StandaloneKubernetes/
│   ├── Dockerfile
│   └── start-selenium-grid-kubernetes.sh
├── Video/
│   ├── Dockerfile
│   ├── entry_point.sh
│   ├── recorder.conf
│   ├── upload.sh
│   ├── uploader.conf
│   ├── validate_endpoint.py
│   ├── video.sh
│   ├── video_graphQLQuery.py
│   ├── video_gridUrl.py
│   ├── video_nodeQuery.py
│   ├── video_ready.py
│   ├── video_recorder.py
│   ├── video_service.py
│   └── video_uploader.py
├── charts/
│   └── selenium-grid/
│       ├── .helmignore
│       ├── CHANGELOG.md
│       ├── CONFIGURATION.md
│       ├── Chart.yaml
│       ├── MIGRATION_INGRESS_NGINX_TO_TRAEFIK.md
│       ├── README.md
│       ├── TESTING.md
│       ├── certs/
│       │   ├── add-cert-helper.sh
│       │   ├── add-jks-helper.sh
│       │   ├── gen-cert-helper.sh
│       │   ├── server.jks
│       │   ├── server.pass
│       │   ├── tls.crt
│       │   └── tls.key
│       ├── configs/
│       │   ├── distributor/
│       │   │   └── distributorProbe.sh
│       │   ├── node/
│       │   │   ├── nodeGridUrl.sh
│       │   │   ├── nodePreStop.sh
│       │   │   ├── nodeProbe.sh
│       │   │   └── nodeProbeReadiness.sh
│       │   ├── router/
│       │   │   ├── routerGraphQLUrl.sh
│       │   │   └── routerProbe.sh
│       │   ├── scrape/
│       │   │   └── selenium-grid.yaml
│       │   └── uploader/
│       │       └── s3/
│       │           └── upload.sh
│       ├── multiple-nodes-platform-relay.yaml
│       ├── multiple-nodes-platform-version.yaml
│       ├── multiple-nodes-platform.yaml
│       ├── templates/
│       │   ├── NOTES.txt
│       │   ├── _helpers.tpl
│       │   ├── _nameHelpers.tpl
│       │   ├── basic-auth-secret.yaml
│       │   ├── chrome-node-deployment.yaml
│       │   ├── chrome-node-hpa.yaml
│       │   ├── chrome-node-scaledjobs.yaml
│       │   ├── chrome-node-service.yaml
│       │   ├── distributor-configmap.yaml
│       │   ├── distributor-deployment.yaml
│       │   ├── distributor-service.yaml
│       │   ├── edge-node-deployment.yaml
│       │   ├── edge-node-hpa.yaml
│       │   ├── edge-node-scaledjob.yaml
│       │   ├── edge-node-service.yaml
│       │   ├── event-bus-configmap.yaml
│       │   ├── event-bus-deployment.yaml
│       │   ├── event-bus-service.yaml
│       │   ├── firefox-node-deployment.yaml
│       │   ├── firefox-node-hpa.yaml
│       │   ├── firefox-node-scaledjob.yaml
│       │   ├── firefox-node-service.yaml
│       │   ├── hub-deployment.yaml
│       │   ├── hub-service.yaml
│       │   ├── ingress.yaml
│       │   ├── jaeger-ingress.yaml
│       │   ├── logging-configmap.yaml
│       │   ├── monitoring-exporter-deployment.yaml
│       │   ├── monitoring-exporter-service.yaml
│       │   ├── monitoring-scape-secret.yaml
│       │   ├── networkpolicy.yaml
│       │   ├── node-configmap.yaml
│       │   ├── patch-keda/
│       │   │   ├── delete-keda-objects-job.yaml
│       │   │   ├── patch-keda-objects-cm.yaml
│       │   │   ├── patch-keda-objects-job.yaml
│       │   │   ├── rbac-role.yaml
│       │   │   └── rbac-rolebinding.yaml
│       │   ├── recorder-configmap.yaml
│       │   ├── relay-node-deployment.yaml
│       │   ├── relay-node-hpa.yaml
│       │   ├── relay-node-scaledjobs.yaml
│       │   ├── relay-node-service.yaml
│       │   ├── router-configmap.yaml
│       │   ├── router-deployment.yaml
│       │   ├── router-service.yaml
│       │   ├── secrets.yaml
│       │   ├── server-configmap.yaml
│       │   ├── serviceaccount.yaml
│       │   ├── session-map-configmap.yaml
│       │   ├── session-map-deployment.yaml
│       │   ├── session-map-service.yaml
│       │   ├── session-queue-configmap.yaml
│       │   ├── session-queue-deployment.yaml
│       │   ├── session-queue-service.yaml
│       │   ├── tls-cert-secret.yaml
│       │   ├── traefik-servers-transport.yaml
│       │   ├── trigger-auth.yaml
│       │   ├── uploader-configmap.yaml
│       │   └── video-manager/
│       │       ├── file-browser-deployment.yaml
│       │       ├── file-browser-ingress.yaml
│       │       └── file-browser-service.yaml
│       └── values.yaml
├── docker-compose-v2-tracing.yml
├── docker-compose-v2.yml
├── docker-compose-v3-basicauth.yml
├── docker-compose-v3-beta-channel.yml
├── docker-compose-v3-dev-channel.yml
├── docker-compose-v3-dev.yml
├── docker-compose-v3-dynamic-grid.yml
├── docker-compose-v3-full-grid-dev.yml
├── docker-compose-v3-full-grid-external-datastore.yml
├── docker-compose-v3-full-grid-nightly.yml
├── docker-compose-v3-full-grid-secure.yml
├── docker-compose-v3-full-grid-swarm.yml
├── docker-compose-v3-full-grid-tracing.yml
├── docker-compose-v3-full-grid.yml
├── docker-compose-v3-node-all-browsers.yml
├── docker-compose-v3-swarm.yml
├── docker-compose-v3-tracing.yml
├── docker-compose-v3-video-in-node.yml
├── docker-compose-v3-video-upload-dynamic-grid.yml
├── docker-compose-v3-video-upload-standalone.yml
├── docker-compose-v3-video-upload.yml
├── docker-compose-v3-video.yml
├── docker-compose-v3.yml
├── generate_chart_changelog.sh
├── generate_release_notes.sh
├── generate_sbom.sh
├── kubernetes/
│   ├── DynamicGrid/
│   │   ├── BaseConfig/
│   │   │   ├── configmap.yaml
│   │   │   ├── pvc.yaml
│   │   │   └── rbac.yaml
│   │   ├── Hub_Node/
│   │   │   ├── hub-deployment.yaml
│   │   │   ├── hub-svc.yaml
│   │   │   └── node-kubernetes-deployment.yaml
│   │   ├── README.md
│   │   └── Standalone/
│   │       └── standalone-kubernetes.yaml
│   ├── Hub_Node/
│   │   ├── hub-deployment.yaml
│   │   ├── hub-svc.yaml
│   │   ├── node-chrome-deployment.yaml
│   │   ├── node-edge-deployment.yaml
│   │   └── node-firefox-deployment.yaml
│   ├── README.md
│   └── Standalone/
│       ├── standalone-chrome.yaml
│       ├── standalone-edge.yaml
│       └── standalone-firefox.yaml
├── renovate.json
├── tag_and_push_browser_images.sh
├── tests/
│   ├── .dockerignore
│   ├── AutoscalingTests/
│   │   ├── __init__.py
│   │   ├── common.py
│   │   ├── test_scale_chaos.py
│   │   └── test_scale_up.py
│   ├── CDPTests/
│   │   ├── .gitignore
│   │   ├── bootstrap.sh
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   └── tests/
│   │       └── Tests.ts
│   ├── Dockerfile
│   ├── Dockerfile.emulator
│   ├── README.md
│   ├── SeleniumJavaTests/
│   │   ├── .gitignore
│   │   ├── README.md
│   │   ├── bootstrap_java.sh
│   │   ├── build.gradle
│   │   ├── gradle/
│   │   │   └── wrapper/
│   │   │       ├── gradle-wrapper.jar
│   │   │       └── gradle-wrapper.properties
│   │   ├── gradlew
│   │   ├── gradlew.bat
│   │   ├── settings.gradle
│   │   └── src/
│   │       └── test/
│   │           └── java/
│   │               └── SeleniumTests.java
│   ├── SeleniumTests/
│   │   └── __init__.py
│   ├── SmokeTests/
│   │   └── __init__.py
│   ├── bootstrap.sh
│   ├── build-backward-compatible/
│   │   ├── add_selenium_version.py
│   │   ├── bootstrap.sh
│   │   ├── browser-matrix.yml
│   │   ├── builder.py
│   │   ├── fetch_chrome_for_testing_version.py
│   │   ├── fetch_firefox_version.py
│   │   ├── fetch_version.py
│   │   ├── firefox-matrix.yml
│   │   ├── selenium-matrix.yml
│   │   └── update_workflow_versions.py
│   ├── charts/
│   │   ├── bootstrap.sh
│   │   ├── ci/
│   │   │   ├── DeploymentAutoscaling-values.yaml
│   │   │   ├── JobAutoscaling-values.yaml
│   │   │   ├── NoAutoscaling-values.yaml
│   │   │   ├── base-auth-ingress-values.yaml
│   │   │   ├── base-recorder-values.yaml
│   │   │   ├── base-resources-values.yaml
│   │   │   ├── base-subPath-values.yaml
│   │   │   ├── base-tls-values.yaml
│   │   │   ├── local-pvc.yaml
│   │   │   ├── nameOverride-values.yaml
│   │   │   ├── the-internet-deployment.yaml
│   │   │   └── uploader.conf
│   │   ├── config/
│   │   │   ├── ct.yaml
│   │   │   └── kind-cluster.yaml
│   │   ├── make/
│   │   │   ├── chart_build.sh
│   │   │   ├── chart_check_env.sh
│   │   │   ├── chart_cluster_cleanup.sh
│   │   │   ├── chart_cluster_setup.sh
│   │   │   ├── chart_release.sh
│   │   │   ├── chart_setup_env.sh
│   │   │   └── chart_test.sh
│   │   ├── refValues/
│   │   │   ├── README.md
│   │   │   ├── aws-s3-upload-secret.yaml
│   │   │   ├── local-pvc-docker-desktop.yaml
│   │   │   ├── sample-aws.yaml
│   │   │   ├── simplex-docker-desktop.yaml
│   │   │   └── simplex-minikube.yaml
│   │   └── templates/
│   │       ├── render/
│   │       │   ├── dummy.yaml
│   │       │   ├── dummy_external.sh
│   │       │   └── dummy_solution.yaml
│   │       ├── test.py
│   │       └── test_scaled_job.py
│   ├── config.toml
│   ├── customCACert/
│   │   ├── Dockerfile
│   │   └── bootstrap.sh
│   ├── docker-compose-v3-dev-arm64.yml
│   ├── docker-compose-v3-event-driven-arm64.yml
│   ├── docker-compose-v3-event-driven-standalone-arm64.yml
│   ├── docker-compose-v3-get-started-arm64.yml
│   ├── docker-compose-v3-test-node-docker.yaml
│   ├── docker-compose-v3-test-node-relay.yml
│   ├── docker-compose-v3-test-parallel.yml
│   ├── docker-compose-v3-test-standalone-docker.yaml
│   ├── docker-compose-v3-test-standalone.yml
│   ├── docker-compose-v3-test-video.yml
│   ├── get_started.py
│   ├── relay_config.toml
│   ├── requirements.txt
│   ├── standalone_docker_config.toml
│   ├── test.py
│   └── test_grid_ui.py
└── update_tag_in_docs_and_files.sh
Download .txt
SYMBOL INDEX (204 symbols across 30 files)

FILE: .keda/scalers/selenium_grid_scaler.go
  type seleniumGridScaler (line 21) | type seleniumGridScaler struct
    method Close (line 194) | func (s *seleniumGridScaler) Close(context.Context) error {
    method GetMetricsAndActivity (line 201) | func (s *seleniumGridScaler) GetMetricsAndActivity(ctx context.Context...
    method GetMetricSpecForScaling (line 226) | func (s *seleniumGridScaler) GetMetricSpecForScaling(context.Context) ...
    method getSessionsQueueLength (line 240) | func (s *seleniumGridScaler) getSessionsQueueLength(ctx context.Contex...
  type seleniumGridScalerMetadata (line 28) | type seleniumGridScalerMetadata struct
  type Platform (line 49) | type Platform struct
  type SeleniumResponse (line 54) | type SeleniumResponse struct
  type Data (line 58) | type Data struct
  type Grid (line 64) | type Grid struct
  type NodesInfo (line 70) | type NodesInfo struct
  type SessionsInfo (line 74) | type SessionsInfo struct
  type Nodes (line 78) | type Nodes
  type ReservedNodes (line 88) | type ReservedNodes struct
  type Sessions (line 94) | type Sessions
  type Slot (line 100) | type Slot struct
  type Stereotypes (line 105) | type Stereotypes
  constant EnableManagedDownloadsCapability (line 110) | EnableManagedDownloadsCapability = "se:downloadsEnabled"
  function filterCapabilities (line 116) | func filterCapabilities(capabilities map[string]interface{}) map[string]...
  function NewSeleniumGridScaler (line 141) | func NewSeleniumGridScaler(config *scalersconfig.ScalerConfig) (Scaler, ...
  function parseCapabilitiesToMap (line 165) | func parseCapabilitiesToMap(_capabilities string) (map[string]interface{...
  function parseSeleniumGridScalerMetadata (line 175) | func parseSeleniumGridScalerMetadata(config *scalersconfig.ScalerConfig)...
  function buildSeleniumGridMetricName (line 212) | func buildSeleniumGridMetricName(meta *seleniumGridScalerMetadata) string {
  function getCapability (line 285) | func getCapability(capability map[string]interface{}, key string) string {
  function getBrowserName (line 293) | func getBrowserName(capability map[string]interface{}) string {
  function getBrowserVersion (line 297) | func getBrowserVersion(capability map[string]interface{}) string {
  function getPlatformName (line 301) | func getPlatformName(capability map[string]interface{}) string {
  function countMatchingSlotsStereotypes (line 305) | func countMatchingSlotsStereotypes(stereotypes Stereotypes, browserName ...
  function countMatchingSessions (line 315) | func countMatchingSessions(sessions Sessions, browserName string, browse...
  function managedDownloadsEnabled (line 330) | func managedDownloadsEnabled(stereotype map[string]interface{}, capabili...
  function extensionCapabilitiesMatch (line 343) | func extensionCapabilitiesMatch(stereotype map[string]interface{}, capab...
  function checkRequestCapabilitiesMatch (line 360) | func checkRequestCapabilitiesMatch(request map[string]interface{}, brows...
  function checkStereotypeCapabilitiesMatch (line 379) | func checkStereotypeCapabilitiesMatch(capability map[string]interface{},...
  function checkNodeReservedSlots (line 398) | func checkNodeReservedSlots(reservedNodes []ReservedNodes, nodeID string...
  function updateOrAddReservedNode (line 407) | func updateOrAddReservedNode(reservedNodes []ReservedNodes, nodeID strin...
  function getCountFromSeleniumResponse (line 419) | func getCountFromSeleniumResponse(b []byte, browserName string, browserV...
  function isSameFamily (line 545) | func isSameFamily(p1, p2 Platform) bool {
  function GetPlatform (line 549) | func GetPlatform(input string) Platform {

FILE: .keda/scalers/selenium_grid_scaler_test.go
  function Test_getCountFromSeleniumResponse (line 12) | func Test_getCountFromSeleniumResponse(t *testing.T) {
  function Test_parseSeleniumGridScalerMetadata (line 3168) | func Test_parseSeleniumGridScalerMetadata(t *testing.T) {

FILE: CHANGELOG/generate-matrix-readme.py
  function archive_old_versions (line 8) | def archive_old_versions():
  function scan_changelog (line 45) | def scan_changelog():
  function generate_readme (line 70) | def generate_readme(matrix):

FILE: Sessions/init.sql
  type sessions_map (line 1) | CREATE TABLE IF NOT EXISTS sessions_map(

FILE: Video/validate_endpoint.py
  function get_timestamp (line 11) | def get_timestamp():
  function create_session (line 30) | def create_session():
  function get_basic_auth (line 36) | def get_basic_auth():
  function validate_endpoint (line 49) | def validate_endpoint(endpoint, graphql_endpoint=False, connection_timeo...
  function main (line 119) | def main():

FILE: Video/video_graphQLQuery.py
  function get_graphql_endpoint (line 22) | def get_graphql_endpoint() -> str:
  function build_basic_auth_header (line 36) | def build_basic_auth_header() -> str | None:
  function poll_session (line 45) | def poll_session(endpoint: str, session_id: str, poll_interval: float) -...
  function _persist_body (line 120) | def _persist_body(session_id: str, body_text: str) -> None:
  function extract_capabilities (line 129) | def extract_capabilities(
  function normalize_filename (line 154) | def normalize_filename(raw_name: str, session_id: str, suffix_enabled: b...
  function derive_allowed_chars (line 182) | def derive_allowed_chars(pattern: str) -> set[str]:
  function main (line 205) | def main(argv: list[str]) -> int:

FILE: Video/video_gridUrl.py
  function get_grid_url (line 5) | def get_grid_url():

FILE: Video/video_nodeQuery.py
  function main (line 11) | def main() -> None:
  function normalize_filename (line 76) | def normalize_filename(filename: str, trim_pattern: str) -> str:

FILE: Video/video_ready.py
  class Handler (line 12) | class Handler(BaseHTTPRequestHandler):
    method do_GET (line 14) | def do_GET(self):
  function graceful_shutdown (line 29) | def graceful_shutdown(signum, frame):

FILE: Video/video_recorder.py
  function main (line 16) | def main():

FILE: Video/video_service.py
  class SessionClosedReason (line 63) | class SessionClosedReason(Enum):
  class SessionStatus (line 72) | class SessionStatus(Enum):
  class UploadTask (line 82) | class UploadTask:
  class SessionState (line 93) | class SessionState:
    method is_failed (line 110) | def is_failed(self) -> bool:
    method duration_seconds (line 119) | def duration_seconds(self) -> Optional[float]:
  class VideoService (line 126) | class VideoService:
    method __init__ (line 129) | def __init__(self):
    method _rename_rclone_env (line 231) | def _rename_rclone_env(self):
    method display (line 240) | def display(self) -> str:
    method video_size (line 244) | def video_size(self) -> str:
    method normalize_filename (line 247) | def normalize_filename(self, filename: str) -> str:
    method get_video_filename (line 259) | def get_video_filename(self, session_id: str, capabilities: dict) -> t...
    method is_failure_event_type (line 294) | def is_failure_event_type(self, event_type: str) -> bool:
    method is_own_node_event (line 299) | def is_own_node_event(self, data: dict) -> bool:
    method wait_for_node_ready (line 322) | async def wait_for_node_ready(self) -> None:
    method start_recording (line 390) | async def start_recording(self, session: SessionState) -> bool:
    method stop_recording (line 464) | async def stop_recording(self, session: SessionState) -> bool:
    method queue_upload (line 498) | async def queue_upload(self, session: SessionState) -> None:
    method process_upload (line 534) | async def process_upload(self, task: UploadTask) -> None:
    method upload_worker (line 572) | async def upload_worker(self) -> None:
    method handle_session_created (line 612) | async def handle_session_created(self, data: dict) -> None:
    method handle_session_closed (line 642) | async def handle_session_closed(self, data: dict) -> None:
    method handle_session_event (line 687) | async def handle_session_event(self, data: dict) -> None:
    method _cleanup_session_delayed (line 716) | async def _cleanup_session_delayed(self, session_id: str, delay: float...
    method subscribe_events (line 728) | async def subscribe_events(self) -> None:
    method wait_for_display (line 827) | async def wait_for_display(self) -> None:
    method cleanup (line 851) | async def cleanup(self) -> None:
    method run (line 881) | async def run(self) -> None:
  function main (line 926) | async def main():

FILE: Video/video_uploader.py
  function main (line 17) | def main():

FILE: tests/AutoscalingTests/common.py
  function get_pod_count (line 45) | def get_pod_count():
  function create_session (line 50) | def create_session(browser_name):
  function wait_for_count_matches (line 60) | def wait_for_count_matches(sessions, timeout=10, interval=5):
  function close_all_sessions (line 77) | def close_all_sessions(sessions):
  function create_sessions_in_parallel (line 84) | def create_sessions_in_parallel(new_request_sessions):
  function randomly_quit_sessions (line 101) | def randomly_quit_sessions(sessions, sublist_size):
  function get_result_file_name (line 112) | def get_result_file_name():
  function export_results_to_csv (line 116) | def export_results_to_csv(output_file, field_names, results):
  function export_results_csv_to_md (line 123) | def export_results_csv_to_md(csv_file, md_file):

FILE: tests/AutoscalingTests/test_scale_chaos.py
  function signal_handler (line 17) | def signal_handler(signum, frame):
  class SeleniumAutoscalingTests (line 26) | class SeleniumAutoscalingTests(unittest.TestCase):
    method test_run_tests (line 27) | def test_run_tests(self):

FILE: tests/AutoscalingTests/test_scale_up.py
  function signal_handler (line 17) | def signal_handler(signum, frame):
  class SeleniumAutoscalingTests (line 26) | class SeleniumAutoscalingTests(unittest.TestCase):
    method test_run_tests (line 27) | def test_run_tests(self):

FILE: tests/CDPTests/tests/Tests.ts
  constant TEST_SITE (line 5) | const TEST_SITE = process.env.TEST_SITE || 'the-internet.herokuapp.com';
  function sleep (line 7) | function sleep(ms: number) {

FILE: tests/SeleniumJavaTests/src/test/java/SeleniumTests.java
  class SeleniumTests (line 20) | class SeleniumTests {
    method setUp (line 24) | @BeforeEach
    method abTestingLinkOpensCorrectPage (line 61) | @Test
    method checkboxesCanBeToggled (line 69) | @Test
    method dropdownSelectionWorks (line 78) | @Test
    method tearDown (line 87) | @AfterEach

FILE: tests/SeleniumTests/__init__.py
  class SeleniumGenericTests (line 67) | class SeleniumGenericTests(unittest.TestCase):
    method test_title (line 69) | def test_title(self):
    method test_with_frames (line 76) | def test_with_frames(self):
    method test_select_from_a_dropdown (line 85) | def test_select_from_a_dropdown(self):
    method test_visit_basic_auth_secured_page (line 101) | def test_visit_basic_auth_secured_page(self):
    method test_play_video (line 107) | def test_play_video(self):
    method test_download_file (line 119) | def test_download_file(self):
    method tearDown (line 135) | def tearDown(self):
  class ChromeTests (line 149) | class ChromeTests(SeleniumGenericTests):
    method setUp (line 150) | def setUp(self):
  class EdgeTests (line 209) | class EdgeTests(SeleniumGenericTests):
    method setUp (line 210) | def setUp(self):
  class FirefoxTests (line 258) | class FirefoxTests(SeleniumGenericTests):
    method setUp (line 259) | def setUp(self):
    method test_title_and_maximize_window (line 311) | def test_title_and_maximize_window(self):
    method test_accept_languages (line 316) | def test_accept_languages(self):
  class Autoscaling (line 331) | class Autoscaling:
    method run (line 332) | def run(self, test_classes):
  class DeploymentAutoscalingTests (line 375) | class DeploymentAutoscalingTests(unittest.TestCase):
    method test_parallel_autoscaling (line 376) | def test_parallel_autoscaling(self):
  class JobAutoscalingTests (line 385) | class JobAutoscalingTests(unittest.TestCase):
    method test_parallel_autoscaling (line 386) | def test_parallel_autoscaling(self):
  class TestPlatform (line 395) | class TestPlatform:
    method add_test_based_platform (line 396) | def add_test_based_platform(self, repeat):

FILE: tests/SmokeTests/__init__.py
  class SmokeTests (line 25) | class SmokeTests(unittest.TestCase):
    method smoke_test_container (line 26) | def smoke_test_container(self, port):
    method client_verify_cert (line 62) | def client_verify_cert(self, port):
  class GridTest (line 70) | class GridTest(SmokeTests):
    method test_grid_is_up (line 71) | def test_grid_is_up(self):

FILE: tests/build-backward-compatible/add_selenium_version.py
  function add_selenium_version (line 9) | def add_selenium_version(version):

FILE: tests/build-backward-compatible/builder.py
  function load_template (line 10) | def load_template(yaml_file):
  function recursive_merge (line 19) | def recursive_merge(dict1, dict2):

FILE: tests/build-backward-compatible/fetch_chrome_for_testing_version.py
  function fetch_chrome_for_testing_versions (line 10) | def fetch_chrome_for_testing_versions():

FILE: tests/build-backward-compatible/fetch_firefox_version.py
  function fetch_firefox_versions (line 10) | def fetch_firefox_versions():

FILE: tests/build-backward-compatible/fetch_version.py
  function fetch_yaml (line 13) | def fetch_yaml(url, local=False):
  function merge_dicts (line 23) | def merge_dicts(dict1, dict2):
  function update_local_yaml (line 31) | def update_local_yaml(local_data, source_data):
  function sort_keys (line 54) | def sort_keys(local_data):
  function main (line 65) | def main():

FILE: tests/build-backward-compatible/update_workflow_versions.py
  function read_browser_matrix (line 14) | def read_browser_matrix(file_path, min_chrome_version, min_firefox_versi...
  function format_version_list (line 86) | def format_version_list(versions):
  function update_workflow_file (line 91) | def update_workflow_file(workflow_file, versions_list):
  function main (line 125) | def main():

FILE: tests/charts/templates/test.py
  function load_template (line 12) | def load_template(yaml_file):
  class ChartTemplateTests (line 22) | class ChartTemplateTests(unittest.TestCase):
    method test_set_affinity (line 23) | def test_set_affinity(self):
    method test_ingress_traefik_annotations (line 48) | def test_ingress_traefik_annotations(self):
    method test_ingress_traefik_servers_transport (line 80) | def test_ingress_traefik_servers_transport(self):
    method test_sub_path_append_to_node_grid_url_and_basic_auth_should_not_include (line 123) | def test_sub_path_append_to_node_grid_url_and_basic_auth_should_not_in...
    method test_sub_path_set_to_grid_env_var (line 134) | def test_sub_path_set_to_grid_env_var(self):
    method test_graphql_url_for_autoscaling_constructed_without_basic_auth_in_url (line 146) | def test_graphql_url_for_autoscaling_constructed_without_basic_auth_in...
    method test_distributor_new_session_thread_pool_size (line 159) | def test_distributor_new_session_thread_pool_size(self):
    method test_disable_ui_set_to_grid_env_var (line 173) | def test_disable_ui_set_to_grid_env_var(self):
    method test_log_level_set_to_logging_config_map (line 185) | def test_log_level_set_to_logging_config_map(self):
    method test_node_port_set_when_service_type_is_node_port (line 217) | def test_node_port_set_when_service_type_is_node_port(self):
    method test_all_metadata_name_is_prefixed_with_release_name (line 235) | def test_all_metadata_name_is_prefixed_with_release_name(self):
    method test_extra_script_import_to_node_configmap (line 245) | def test_extra_script_import_to_node_configmap(self):
    method test_extra_script_import_to_uploader_configmap (line 258) | def test_extra_script_import_to_uploader_configmap(self):
    method test_extra_script_import_to_recorder_configmap (line 269) | def test_extra_script_import_to_recorder_configmap(self):
    method test_upload_conf_mount_to_video_container (line 280) | def test_upload_conf_mount_to_video_container(self):
    method test_terminationGracePeriodSeconds_in_deployment_autoscaling (line 311) | def test_terminationGracePeriodSeconds_in_deployment_autoscaling(self):
    method test_enable_leftovers_cleanup (line 343) | def test_enable_leftovers_cleanup(self):
    method test_enable_tracing (line 356) | def test_enable_tracing(self):
    method test_update_strategy_in_all_components (line 366) | def test_update_strategy_in_all_components(self):
    method test_topologySpreadConstraints_in_all_components (line 398) | def test_topologySpreadConstraints_in_all_components(self):
    method test_not_create_basic_auth_secret_when_nameOverride_is_set (line 422) | def test_not_create_basic_auth_secret_when_nameOverride_is_set(self):
    method test_router_envFrom_secretRef_name_use_external_secret_when_basicAuth_nameOverride_is_set (line 431) | def test_router_envFrom_secretRef_name_use_external_secret_when_basicA...
    method test_scaler_triggers_authenticationRef_name_is_added (line 455) | def test_scaler_triggers_authenticationRef_name_is_added(self):
    method test_scaler_triggers_parameter_nodeMaxSessions_global_and_individual_value (line 468) | def test_scaler_triggers_parameter_nodeMaxSessions_global_and_individu...
    method test_monitoring_exporter_tolerations (line 498) | def test_monitoring_exporter_tolerations(self):

FILE: tests/charts/templates/test_scaled_job.py
  function load_template (line 11) | def load_template(yaml_file):
  class ScaledJobTemplateTests (line 21) | class ScaledJobTemplateTests(unittest.TestCase):
    method test_scaled_job_has_zero_limits (line 22) | def test_scaled_job_has_zero_limits(self):

FILE: tests/get_started.py
  function run_browser_instance (line 25) | def run_browser_instance(browser, grid_url):

FILE: tests/test.py
  function clean_up (line 14) | def clean_up():
  function signal_handler (line 40) | def signal_handler(signum, frame):
  function get_platform (line 130) | def get_platform():
  function launch_application (line 140) | def launch_application(network_name):
  function launch_hub (line 181) | def launch_hub(network_name):
  function create_network (line 216) | def create_network(network_name):
  function prune_networks (line 220) | def prune_networks():
  function launch_container (line 224) | def launch_container(container, **kwargs):
  function set_from_image_base_for_standalone (line 279) | def set_from_image_base_for_standalone(container):
  function get_build_path (line 285) | def get_build_path(container):
  function standalone_browser_container_matches (line 293) | def standalone_browser_container_matches(container):

FILE: tests/test_grid_ui.py
  function run_browser_instance (line 21) | def run_browser_instance(browser):
Condensed preview — 2070 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,178K chars).
[
  {
    "path": ".circleci/config.bak",
    "chars": 11806,
    "preview": "version: 2.1\n\nworkflows:\n  build-and-test-multi-arch:\n    jobs:\n      - kubernetes-test:\n          name: \"K8s test - Aut"
  },
  {
    "path": ".editorconfig",
    "chars": 355,
    "preview": "# EditorConfig is awesome: http://EditorConfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with"
  },
  {
    "path": ".ffmpeg/Dockerfile",
    "chars": 3641,
    "preview": "FROM ubuntu:noble AS builder\nARG FFMPEG_VERSION=\"8.0\"\nARG RCLONE_VER=\"v1.73-stable\"\nARG GO_VERSION=\"latest\"\n#ARG GO_CRYP"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2511,
    "preview": "name: 🐛 Bug Report\ndescription: File a bug report\ntitle: \"[🐛 Bug]: \"\nlabels: [bug, needs-triaging]\nbody:\n  - type: markd"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 585,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: 💬 Selenium Community Support and Questions\n    url: https://www.sel"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_proposal.yml",
    "chars": 974,
    "preview": "name: 🚀 Feature Proposal\ndescription: Propose a feature\ntitle: \"[🚀 Feature]: \"\nlabels: [feature, needs-triaging]\nbody:\n "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1657,
    "preview": "<!-- Thanks for sending us a PR to improve this project! If you are adding a \nfeature or fixing a bug, and this needs mo"
  },
  {
    "path": ".github/actions/get-latest-upstream/action.yml",
    "chars": 2012,
    "preview": "name: Get Latest Upstream\ndescription: Get the latest upstream release of Selenium\ninputs:\n  release:\n    description: '"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 1101,
    "preview": "version: 2\nupdates:\n- package-ecosystem: docker\n  directory: \"/StandaloneChrome\"\n  schedule:\n    interval: daily\n    tim"
  },
  {
    "path": ".github/label-commenter-config.yml",
    "chars": 5191,
    "preview": "# Configuration for Label Commenter - https://github.com/peaceiris/actions-label-commenter\nlabels:\n  - name: needs-triag"
  },
  {
    "path": ".github/workflows/build-ffmpeg.yml",
    "chars": 2089,
    "preview": "name: Build and Deploy FFmpeg\n\non:\n  push:\n    branches:\n      - trunk\n    paths:\n      - '.ffmpeg/Dockerfile'\n  pull_re"
  },
  {
    "path": ".github/workflows/build-test.yml",
    "chars": 3388,
    "preview": "name: Build & test\n\non:\n  workflow_call:\n    secrets:\n      DOCKER_USERNAME:\n        required: false\n      DOCKER_PASSWO"
  },
  {
    "path": ".github/workflows/create-changelog-pr.yml",
    "chars": 2981,
    "preview": "name: Create changelog PR\n\non:\n  workflow_call:\n    inputs:\n      grid-version:\n        required: true\n        type: str"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "chars": 8614,
    "preview": "name: Deploys\n\non:\n  workflow_dispatch:\n    inputs:\n      stable:\n        description: 'Use upstream stable build'\n     "
  },
  {
    "path": ".github/workflows/docker-test.yml",
    "chars": 10476,
    "preview": "name: Test Docker Selenium\n\non:\n  workflow_call:\n    inputs:\n      release:\n        description: 'Test a new release pro"
  },
  {
    "path": ".github/workflows/helm-chart-release.yml",
    "chars": 5186,
    "preview": "name: Release Charts\nconcurrency:\n  group: ${{ github.workflow }}\n\non:\n  push:\n    branches:\n      - trunk\n    paths:\n  "
  },
  {
    "path": ".github/workflows/helm-chart-test.yml",
    "chars": 10338,
    "preview": "name: Test Helm Charts\n\non:\n  workflow_call:\n    secrets:\n      DOCKER_USERNAME:\n        required: false\n      DOCKER_PA"
  },
  {
    "path": ".github/workflows/k8s-scaling-test.yml",
    "chars": 10379,
    "preview": "name: Test Grid Autoscaling in Kubernetes\n\non:\n  workflow_call:\n    secrets:\n      DOCKER_USERNAME:\n        required: fa"
  },
  {
    "path": ".github/workflows/label-commenter.yml",
    "chars": 370,
    "preview": "# Configuration for Label Commenter - https://github.com/peaceiris/actions-label-commenter\nname: Label Commenter\n\non:\n  "
  },
  {
    "path": ".github/workflows/lock.yml",
    "chars": 630,
    "preview": "# Configuration for Lock Threads - https://github.com/dessant/lock-threads\nname: 'Lock Issues'\n\non:\n  workflow_dispatch:"
  },
  {
    "path": ".github/workflows/nightly.yml",
    "chars": 5852,
    "preview": "name: Nightly\non:\n  workflow_dispatch:\n    inputs:\n      skip-test:\n        description: 'Skip the tests'\n        requir"
  },
  {
    "path": ".github/workflows/release-all-browser-versions.yml",
    "chars": 4090,
    "preview": "name: Deploy all browser versions\n\non:\n  workflow_dispatch:\n    inputs:\n      stable:\n        description: 'Use upstream"
  },
  {
    "path": ".github/workflows/release-chrome-for-testing-versions.yml",
    "chars": 6486,
    "preview": "name: Deploy specific Chrome for Testing version\n\non:\n  workflow_dispatch:\n    inputs:\n      stable:\n        description"
  },
  {
    "path": ".github/workflows/release-chrome-versions.yml",
    "chars": 6487,
    "preview": "name: Deploy specific Chrome version\n\non:\n  workflow_dispatch:\n    inputs:\n      stable:\n        description: 'Use upstr"
  },
  {
    "path": ".github/workflows/release-edge-versions.yml",
    "chars": 6383,
    "preview": "name: Deploy specific Edge version\n\non:\n  workflow_dispatch:\n    inputs:\n      stable:\n        description: 'Use upstrea"
  },
  {
    "path": ".github/workflows/release-firefox-versions.yml",
    "chars": 6606,
    "preview": "name: Deploy specific Firefox version\n\non:\n  workflow_dispatch:\n    inputs:\n      stable:\n        description: 'Use upst"
  },
  {
    "path": ".github/workflows/release-preparation.yml",
    "chars": 2569,
    "preview": "name: Release Preparation\n\non:\n  workflow_call:\n    inputs:\n      grid-version:\n        required: true\n        default: "
  },
  {
    "path": ".github/workflows/rerun-failed.yml",
    "chars": 1162,
    "preview": "name: Rerun Workflows\n\non:\n  workflow_dispatch:\n    inputs:\n      runId:\n        description: 'The ID of workflow to rer"
  },
  {
    "path": ".github/workflows/scan-dockerfile.yml",
    "chars": 1740,
    "preview": "name: Scan Dockerfile vulnerabilities\nconcurrency:\n  group: ${{ github.workflow }}\n\non:\n  push:\n    paths:\n      - '**/D"
  },
  {
    "path": ".github/workflows/update-chart-readme.yml",
    "chars": 1549,
    "preview": "name: Update chart configuration table\n\non:\n  push:\n    branches:\n      - 'renovate/*'\n  pull_request:\n    types:\n      "
  },
  {
    "path": ".github/workflows/update-dev-beta-browser-images.yml",
    "chars": 6374,
    "preview": "name: Update Dev/Beta Browser Images\n\non:\n  workflow_dispatch:\n    inputs:\n      rerunFailedOnly:\n        description: '"
  },
  {
    "path": ".gitignore",
    "chars": 2149,
    "preview": "tmp/\n*_image/\nnode_modules/\n.idea/\n*.iml\n# Sed backup files.\n*-e\n# Vim swap files.\n*.swp\n# Selenium Server Dev\nselenium_"
  },
  {
    "path": ".gitpod.yml",
    "chars": 344,
    "preview": "tasks:\n  - name: Show README link and build instructions\n    before: | \n      echo \"Try out Selenium Grid in Docker by f"
  },
  {
    "path": ".keda/README.md",
    "chars": 4861,
    "preview": "# Introduction\n\nSelenium Grid Scaler is a built-in scaler is maintained in upstream KEDA [repository](https://github.com"
  },
  {
    "path": ".keda/results_test_k8s_autoscaling_deployment_count.md",
    "chars": 4487,
    "preview": "| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total runn"
  },
  {
    "path": ".keda/results_test_k8s_autoscaling_deployment_count_in_chaos.md",
    "chars": 4487,
    "preview": "| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total runn"
  },
  {
    "path": ".keda/results_test_k8s_autoscaling_deployment_count_with_node_max_sessions.md",
    "chars": 4487,
    "preview": "| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total runn"
  },
  {
    "path": ".keda/results_test_k8s_autoscaling_job_count_strategy_default.md",
    "chars": 4487,
    "preview": "| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total runn"
  },
  {
    "path": ".keda/results_test_k8s_autoscaling_job_count_strategy_default_in_chaos.md",
    "chars": 4487,
    "preview": "| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total runn"
  },
  {
    "path": ".keda/results_test_k8s_autoscaling_job_count_strategy_default_with_node_max_sessions.md",
    "chars": 4487,
    "preview": "| Iteration | New request sessions | Sessions created time | Sessions failed to create | New pods scaled up | Total runn"
  },
  {
    "path": ".keda/scalers/selenium-grid-scaler.md",
    "chars": 19624,
    "preview": "+++\ntitle = \"Selenium Grid Scaler\"\navailability = \"v2.4+\"\nmaintainer = \"Volvo Cars, SeleniumHQ\"\ncategory = \"Testing\"\ndes"
  },
  {
    "path": ".keda/scalers/selenium_grid_scaler.go",
    "chars": 23110,
    "preview": "package scalers\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.c"
  },
  {
    "path": ".keda/scalers/selenium_grid_scaler_test.go",
    "chars": 143351,
    "preview": "package scalers\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/go-logr/logr\"\n\n\t\"github.com/kedacore/keda/v2/pkg/scalers/s"
  },
  {
    "path": "Base/Dockerfile",
    "chars": 11221,
    "preview": "FROM ubuntu:noble@sha256:186072bba1b2f436cbb91ef2567abca677337cfc786c86e107d25b7072feef0c\nARG AUTHORS=SeleniumHQ\nLABEL a"
  },
  {
    "path": "Base/check-grid.sh",
    "chars": 740,
    "preview": "#!/usr/bin/env bash\n# check-grid.sh\n\nset -e\n\nHOST=\"localhost\"\nPORT=\"4444\"\ndeclare -a extra_args\nif [ -n \"${SE_ROUTER_USE"
  },
  {
    "path": "Base/entry_point.sh",
    "chars": 1406,
    "preview": "#!/usr/bin/env bash\n\nNODE_CONFIG_DIRECTORY=${NODE_CONFIG_DIRECTORY:-\"/opt/bin\"}\n#======================================="
  },
  {
    "path": "Base/handle_heap_dump.sh",
    "chars": 779,
    "preview": "#!/usr/bin/env bash\n\nSELENIUM_SERVER_PID=\"$(ps -ef | grep \"selenium-server.jar\" | grep -v grep | awk '{print $2}')\"\nLOG_"
  },
  {
    "path": "Base/mask",
    "chars": 476,
    "preview": "#!/usr/bin/env bash\n\nSE_MASK_SECRETS_MIN_LENGTH=${SE_MASK_SECRETS_MIN_LENGTH:-3}\nSE_MASK_SECRETS_KEEP_LAST=${SE_MASK_SEC"
  },
  {
    "path": "Base/supervisord.conf",
    "chars": 1704,
    "preview": "; Documentation of this file format -> http://supervisord.org/configuration.html\n\n[supervisord]\nchildlogdir=%(ENV_SE_SUP"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_113.md",
    "chars": 1376,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_114.md",
    "chars": 1383,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_115.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_116.md",
    "chars": 1376,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_117.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_118.md",
    "chars": 1376,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_119.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_120.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_121.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_122.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_123.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_124.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_125.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_126.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_127.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_128.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_129.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_130.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_131.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_132.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_133.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_134.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_135.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_136.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_137.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_138.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_139.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_140.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_141.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_142.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_143.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_144.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome-for-testing_145.md",
    "chars": 1388,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome-for-testing true\nTagging images for browser c"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_100.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_101.md",
    "chars": 1184,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_102.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_103.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_104.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_105.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_106.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_107.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_108.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_109.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_110.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_111.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_112.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_113.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_114.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_115.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_116.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_117.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_118.md",
    "chars": 1191,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_119.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_120.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_121.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_122.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_123.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_124.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_125.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_126.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_127.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_128.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_129.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_130.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_131.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_132.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_133.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_134.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_135.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_136.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_137.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_138.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_139.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_140.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_141.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_142.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_143.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_144.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_145.md",
    "chars": 1196,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_95.md",
    "chars": 1160,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_96.md",
    "chars": 1167,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_97.md",
    "chars": 1160,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_98.md",
    "chars": 1172,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/chrome_99.md",
    "chars": 1160,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false chrome true\nTagging images for browser chrome, versi"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_114.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_115.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_116.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_117.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_118.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_119.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_120.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_121.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_122.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_123.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_124.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_125.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_126.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_127.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_128.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_129.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_130.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_131.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_132.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_133.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_134.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_135.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_136.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_137.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_138.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_139.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_140.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_141.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_142.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_143.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_144.md",
    "chars": 1144,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/edge_145.md",
    "chars": 1132,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false edge true\nTagging images for browser edge, version 4"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_100.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_101.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_102.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_103.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_104.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_105.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_106.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_107.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_108.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_109.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_110.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_111.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_112.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_113.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_114.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_115.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_116.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_117.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_118.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_119.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_120.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_121.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_122.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_123.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_124.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_125.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_126.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_127.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_128.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_129.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_130.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_131.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_132.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_133.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  },
  {
    "path": "CHANGELOG/4.41.0/firefox_134.md",
    "chars": 1108,
    "preview": "```\n./tag_and_push_browser_images.sh 4.41.0 20260222 selenium false firefox true\nTagging images for browser firefox, ver"
  }
]

// ... and 1870 more files (download for full content)

About this extraction

This page contains the full source code of the SeleniumHQ/docker-selenium GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 2070 files (3.7 MB), approximately 1.1M tokens, and a symbol index with 204 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!