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
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
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.