├── .codacy.yml ├── .dockerignore ├── .editorconfig ├── .env ├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab ├── CODEOWNERS ├── changelog.yml ├── ci │ ├── _common.gitlab-ci.yml │ ├── _rules.gitlab-ci.yml │ ├── build.gitlab-ci.yml │ ├── coverage.gitlab-ci.yml │ ├── docs.gitlab-ci.yml │ ├── fips-helper-binaries.gitlab-ci.yml │ ├── package.gitlab-ci.yml │ ├── postrelease.gitlab-ci.yml │ ├── prebuild.gitlab-ci.yml │ ├── prepare.gitlab-ci.yml │ ├── release.gitlab-ci.yml │ └── test.gitlab-ci.yml ├── dependency_decisions.yml ├── issue_templates │ ├── Bug.md │ ├── Default.md │ ├── Feature Proposal.md │ ├── Security developer workflow.md │ └── trainee-backend-maintainer.md ├── merge_request_templates │ ├── Default.md │ ├── Documentation.md │ └── Security Release.md └── route-map.yml ├── .golangci.yml ├── .goreleaser.yml ├── .markdownlint.yml ├── .tool-versions ├── .vale.ini ├── .vscode └── extensions.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Jenkinsfile ├── LICENSE ├── Makefile ├── Makefile.build.mk ├── Makefile.package.mk ├── Makefile.runner_helper.mk ├── NOTICE ├── PROCESS.md ├── Procfile ├── README.md ├── VERSION ├── Vagrantfile ├── apps └── gitlab-runner-helper │ └── main.go ├── boring ├── boring.go └── notboring.go ├── build-and-push-to-dockerhub.bash ├── build-and-tar.bash ├── cache ├── adapter.go ├── adapter_test.go ├── azure │ ├── adapter.go │ ├── adapter_test.go │ ├── azure.go │ ├── azure_test.go │ ├── credentials_resolver.go │ ├── credentials_resolver_test.go │ └── mock_credentialsResolver.go ├── cache.go ├── cache_test.go ├── credentials_adapter.go ├── credentials_adapter_test.go ├── gcs │ ├── adapter.go │ ├── adapter_test.go │ ├── credentials_resolver.go │ ├── credentials_resolver_test.go │ └── mock_credentialsResolver.go ├── mock_Adapter.go ├── mock_CredentialsAdapter.go ├── s3 │ ├── adapter.go │ ├── adapter_test.go │ ├── bucket_location_tripper.go │ ├── credentials_adapter.go │ ├── credentials_adapter_test.go │ ├── minio.go │ ├── minio_test.go │ └── mock_minioClient.go └── test │ └── adapter.go ├── ci ├── .colors ├── .test-failures.servercore1809.txt ├── .test-failures.servercore2004.txt ├── .test-failures.servercore20H2.txt ├── _build_ci_image_common ├── build_ci_image ├── build_go_fips_image ├── build_helper_docker ├── build_release_windows_images.ps1 ├── docker_commands ├── package ├── push_packagecloud ├── release_docker_images ├── release_helper_docker_images ├── release_packagecloud ├── release_s3 ├── test_windows.ps1 ├── touch_make_dependencies ├── upstream_commit_ref └── version ├── commands ├── builds_helper.go ├── builds_helper_integration_test.go ├── builds_helper_test.go ├── config.go ├── config_test.go ├── config_unix.go ├── config_windows.go ├── constants.go ├── exec.go ├── health_helper.go ├── helpers │ ├── archive │ │ ├── archive.go │ │ ├── archive_test.go │ │ ├── fastzip │ │ │ ├── zip_fastzip_archiver.go │ │ │ └── zip_fastzip_extractor.go │ │ ├── gziplegacy │ │ │ └── gzip_legacy_archiver.go │ │ ├── mock_Archiver.go │ │ ├── mock_Extractor.go │ │ ├── raw │ │ │ └── raw_archiver.go │ │ └── ziplegacy │ │ │ ├── zip_legacy_archiver.go │ │ │ └── zip_legacy_extractor.go │ ├── archiver.go │ ├── archiver_test.go │ ├── artifacts_downloader.go │ ├── artifacts_downloader_test.go │ ├── artifacts_test.go │ ├── artifacts_uploader.go │ ├── artifacts_uploader_integration_test.go │ ├── artifacts_uploader_test.go │ ├── cache_archiver.go │ ├── cache_archiver_integration_test.go │ ├── cache_client.go │ ├── cache_extractor.go │ ├── cache_extractor_test.go │ ├── cache_init.go │ ├── cache_init_integration_test.go │ ├── file_archiver.go │ ├── file_archiver_test.go │ ├── health_check.go │ ├── health_check_integration_test.go │ ├── helpers_archiver_test.go │ ├── helpers_cache_archiver_test.go │ ├── meter │ │ ├── formatters.go │ │ ├── formatters_test.go │ │ ├── meter.go │ │ ├── reader.go │ │ ├── reader_test.go │ │ ├── writer.go │ │ └── writer_test.go │ ├── mock_logOutputWriter.go │ ├── mock_logStreamProvider.go │ ├── mock_readSeekCloser.go │ ├── read_logs.go │ ├── read_logs_test.go │ ├── retry_helper.go │ ├── retry_helper_test.go │ └── testdata │ │ └── test-artifacts │ │ ├── file-0 │ │ ├── file-1 │ │ ├── file-2 │ │ ├── file-3 │ │ └── file-4 ├── helpers_register_test.go ├── list.go ├── multi.go ├── multi_test.go ├── register.go ├── register_integration_test.go ├── register_test.go ├── register_windows_test.go ├── service.go ├── service_darwin.go ├── service_integration_test.go ├── service_linux.go ├── service_portable.go ├── service_windows.go ├── single.go ├── single_test.go ├── unregister.go ├── user_mode_warning.go └── verify.go ├── common ├── allowed_images.go ├── allowed_images_test.go ├── build.go ├── build_logger.go ├── build_logger_test.go ├── build_test.go ├── buildtest │ ├── abort.go │ ├── binary.go │ ├── cleanup.go │ ├── job_output_limit.go │ ├── masking.go │ ├── sections.go │ └── test.go ├── command.go ├── config.go ├── config_test.go ├── consts.go ├── executor.go ├── executor_test.go ├── mock_Commander.go ├── mock_Executor.go ├── mock_ExecutorData.go ├── mock_ExecutorProvider.go ├── mock_FailuresCollector.go ├── mock_JobTrace.go ├── mock_Network.go ├── mock_SecretResolver.go ├── mock_SecretResolverRegistry.go ├── mock_SecretsResolver.go ├── mock_Shell.go ├── mock_logger.go ├── mock_masker.go ├── network.go ├── network_test.go ├── secrets.go ├── secrets_test.go ├── shell.go ├── support.go ├── test.go ├── trace.go ├── variables.go ├── variables_test.go └── version.go ├── config.toml.example ├── dockerfiles ├── ci │ ├── Dockerfile │ ├── go.fips.Dockerfile │ └── install_git_lfs ├── fips │ ├── helper.fips.Dockerfile │ └── runner.fips.Dockerfile ├── runner-helper │ ├── Dockerfile.alpine │ ├── Dockerfile.fips │ ├── Dockerfile.ubuntu │ ├── Dockerfile.x86_64_servercore │ ├── binaries │ │ └── .gitkeep │ ├── helpers │ │ ├── checksum.ps1 │ │ ├── entrypoint │ │ └── entrypoint.cmd │ └── scripts │ │ └── gitlab-runner-build └── runner │ ├── alpine │ ├── Dockerfile │ ├── README.md │ ├── entrypoint │ └── install-gitlab-runner │ ├── install-deps │ ├── ubi-fips │ ├── Dockerfile │ ├── README.md │ ├── entrypoint │ └── install-gitlab-runner │ └── ubuntu │ ├── Dockerfile │ ├── README.md │ ├── entrypoint │ └── install-gitlab-runner ├── docs ├── .markdownlint │ └── markdownlint-no-trailing-spaces.yml ├── .vale │ └── gitlab │ │ ├── Admin.yml │ │ ├── AlertBoxStyle.yml │ │ ├── BadgeCapitalization.yml │ │ ├── British.yml │ │ ├── CodeblockFences.yml │ │ ├── CurlStringsQuoted.yml │ │ ├── CurrentStatus.yml │ │ ├── DefaultBranch.yml │ │ ├── Dropdown.yml │ │ ├── ElementDescriptors.yml │ │ ├── FirstPerson.yml │ │ ├── FutureTense.yml │ │ ├── InclusionAbleism.yml │ │ ├── InclusionCultural.yml │ │ ├── InclusionGender.yml │ │ ├── InternalLinkCase.yml │ │ ├── InternalLinkExtension.yml │ │ ├── InternalLinkFormat.yml │ │ ├── LatinTerms.yml │ │ ├── MeaningfulLinkWords.yml │ │ ├── MergeConflictMarkers.yml │ │ ├── NonStandardQuotes.yml │ │ ├── OutdatedVersions.yml │ │ ├── OxfordComma.yml │ │ ├── Possessive.yml │ │ ├── ReadingLevel.yml │ │ ├── ReferenceLinks.yml │ │ ├── RelativeLinks.yml │ │ ├── RelativeLinksDoubleSlashes.yml │ │ ├── Repetition.yml │ │ ├── SentenceLength.yml │ │ ├── SentenceSpacing.yml │ │ ├── Simplicity.yml │ │ ├── Spelling.yml │ │ ├── SubstitutionSuggestions.yml │ │ ├── SubstitutionWarning.yml │ │ ├── Substitutions.yml │ │ ├── ToDo.yml │ │ ├── UnclearAntecedent.yml │ │ ├── Uppercase.yml │ │ ├── VersionText.yml │ │ ├── Wordy.yml │ │ └── spelling-exceptions.txt ├── best_practice │ └── index.md ├── commands │ └── index.md ├── configuration │ ├── advanced-configuration.md │ ├── autoscale.md │ ├── configuring_runner_openshift.md │ ├── configuring_runner_operator.md │ ├── feature-flags.md │ ├── gpus.md │ ├── img │ │ ├── autoscale-example.png │ │ ├── autoscale-state-chart.png │ │ └── runner_fargate_driver_ssh.png │ ├── index.md │ ├── init.md │ ├── macos_setup.md │ ├── proxy.md │ ├── runner_autoscale_aws │ │ └── index.md │ ├── runner_autoscale_aws_fargate │ │ └── index.md │ ├── speed_up_job_execution.md │ └── tls-self-signed.md ├── development │ ├── add-windows-version.md │ ├── index.md │ └── reviewing-gitlab-runner.md ├── examples │ └── gitlab.md ├── executors │ ├── custom.md │ ├── custom_examples │ │ ├── libvirt.md │ │ └── lxd.md │ ├── docker.md │ ├── docker_machine.md │ ├── index.md │ ├── kubernetes.md │ ├── parallels.md │ ├── shell.md │ ├── ssh.md │ └── virtualbox.md ├── faq │ └── index.md ├── fleet_scaling │ └── index.md ├── index.md ├── install │ ├── bleeding-edge.md │ ├── docker.md │ ├── freebsd.md │ ├── gpg-keys │ │ └── 9CE45ABC880721D4.pub.gpg │ ├── img │ │ ├── openshift_allitems_v13_3.png │ │ ├── openshift_installoperator_v13_3.png │ │ └── openshift_success_v13_3.png │ ├── index.md │ ├── kubernetes-agent.md │ ├── kubernetes.md │ ├── linux-manually.md │ ├── linux-repository.md │ ├── old.md │ ├── openshift.md │ ├── operator.md │ ├── osx.md │ └── windows.md ├── monitoring │ └── index.md ├── register │ └── index.md ├── security │ └── index.md └── shells │ └── index.md ├── executors ├── abstract.go ├── anka │ ├── ankaCloudClient │ │ ├── client.go │ │ └── types.go │ ├── anka_connector.go │ └── executor_anka.go ├── custom │ ├── api │ │ ├── config.go │ │ └── const.go │ ├── command │ │ ├── command.go │ │ ├── command_test.go │ │ ├── errors.go │ │ └── mock_Command.go │ ├── config.go │ ├── config_test.go │ ├── consts.go │ ├── custom.go │ ├── custom_test.go │ ├── integration_test.go │ ├── terminal.go │ ├── terminal_test.go │ └── testdata │ │ └── test_executor │ │ ├── .gitignore │ │ └── main.go ├── default_executor_provider.go ├── docker │ ├── config_updater.go │ ├── config_updater_test.go │ ├── consts.go │ ├── docker.go │ ├── docker_command.go │ ├── docker_command_integration_test.go │ ├── docker_command_windows.go │ ├── docker_ssh.go │ ├── docker_test.go │ ├── internal │ │ ├── exec │ │ │ ├── exec.go │ │ │ ├── exec_test.go │ │ │ ├── mock_Docker.go │ │ │ ├── mock_conn.go │ │ │ └── mock_reader.go │ │ ├── labels │ │ │ ├── labels.go │ │ │ ├── labels_test.go │ │ │ └── mock_Labeler.go │ │ ├── networks │ │ │ ├── manager.go │ │ │ ├── manager_integration_test.go │ │ │ ├── manager_test.go │ │ │ ├── mock_Manager.go │ │ │ ├── mock_debugLogger.go │ │ │ └── utils.go │ │ ├── pull │ │ │ ├── manager.go │ │ │ ├── manager_test.go │ │ │ ├── mock_Manager.go │ │ │ └── mock_pullLogger.go │ │ ├── user │ │ │ ├── mock_Inspect.go │ │ │ ├── user.go │ │ │ └── user_test.go │ │ ├── volumes │ │ │ ├── manager.go │ │ │ ├── manager_integration_test.go │ │ │ ├── manager_integration_unix_test.go │ │ │ ├── manager_integration_windows_test.go │ │ │ ├── manager_test.go │ │ │ ├── manager_windows_test.go │ │ │ ├── mock_Manager.go │ │ │ ├── mock_debugLogger.go │ │ │ ├── parser │ │ │ │ ├── base_parser.go │ │ │ │ ├── errors.go │ │ │ │ ├── linux_parser.go │ │ │ │ ├── linux_parser_test.go │ │ │ │ ├── mock_Parser.go │ │ │ │ ├── mock_Path.go │ │ │ │ ├── parser.go │ │ │ │ ├── volume.go │ │ │ │ ├── volume_test.go │ │ │ │ ├── windows_parser.go │ │ │ │ └── windows_parser_test.go │ │ │ ├── permission │ │ │ │ ├── linux_set.go │ │ │ │ ├── linux_set_integration_test.go │ │ │ │ ├── linux_set_test.go │ │ │ │ ├── mock_Setter.go │ │ │ │ ├── set.go │ │ │ │ └── windows_set.go │ │ │ ├── utils.go │ │ │ ├── utils_test.go │ │ │ └── utils_windows_test.go │ │ └── wait │ │ │ ├── mock_KillWaiter.go │ │ │ ├── mock_Waiter.go │ │ │ ├── wait.go │ │ │ └── wait_test.go │ ├── labeler.go │ ├── machine │ │ ├── collector.go │ │ ├── collector_test.go │ │ ├── consts.go │ │ ├── coordinator.go │ │ ├── coordinator_test.go │ │ ├── data.go │ │ ├── details.go │ │ ├── details_test.go │ │ ├── idle_limit_strategy.go │ │ ├── idle_limit_strategy_test.go │ │ ├── machine.go │ │ ├── machine_test.go │ │ ├── name.go │ │ ├── name_test.go │ │ ├── provider.go │ │ ├── provider_test.go │ │ ├── state.go │ │ ├── terminal.go │ │ └── terminal_test.go │ ├── network.go │ ├── pull.go │ ├── terminal.go │ ├── terminal_integration_test.go │ ├── terminal_test.go │ ├── tty.go │ └── volume.go ├── init.go ├── kubernetes │ ├── exec.go │ ├── exec_test.go │ ├── feature.go │ ├── feature_test.go │ ├── helpers_kubernetes_test.go │ ├── host_aliases.go │ ├── host_aliases_test.go │ ├── internal │ │ └── pull │ │ │ ├── errors.go │ │ │ ├── manager.go │ │ │ ├── manager_test.go │ │ │ ├── mock_Manager.go │ │ │ └── mock_pullLogger.go │ ├── kubernetes.go │ ├── kubernetes_integration_test.go │ ├── kubernetes_test.go │ ├── log_processor.go │ ├── log_processor_test.go │ ├── mock_RemoteExecutor.go │ ├── mock_backoffCalculator.go │ ├── mock_featureChecker.go │ ├── mock_logProcessor.go │ ├── mock_logStreamer.go │ ├── overwrites.go │ ├── overwrites_test.go │ ├── service_proxy.go │ ├── service_proxy_test.go │ ├── terminal.go │ ├── util.go │ └── util_test.go ├── parallels │ ├── parallels.go │ ├── parallels_integration_test.go │ └── parallels_test.go ├── shell │ ├── shell.go │ ├── shell_integration_test.go │ ├── shell_terminal.go │ └── shell_test.go ├── ssh │ ├── ssh.go │ └── ssh_test.go ├── virtualbox │ ├── virtualbox.go │ ├── virtualbox_integration_test.go │ └── virtualbox_test.go └── vm │ ├── vm.go │ └── vm_test.go ├── go.mod ├── go.sum ├── helpers ├── ansi_colors.go ├── archives │ ├── gzip_create.go │ ├── gzip_create_test.go │ ├── path_check_helper.go │ ├── path_check_helper_test.go │ ├── path_error_tracker.go │ ├── path_error_tracker_test.go │ ├── zip_create.go │ ├── zip_create_test.go │ ├── zip_create_unix_test.go │ ├── zip_create_windows_test.go │ ├── zip_extra.go │ ├── zip_extra_test.go │ ├── zip_extra_unix.go │ ├── zip_extra_windows.go │ ├── zip_extract.go │ └── zip_extract_test.go ├── build_section.go ├── build_section_test.go ├── certificate │ ├── certificate.go │ ├── mock_Generator.go │ ├── x509.go │ └── x509_test.go ├── cli │ ├── cpuprofile.go │ ├── fix_home.go │ ├── init_cli.go │ ├── init_cli_windows.go │ ├── runtime_platform.go │ └── warn_on_bool.go ├── container │ ├── helperimage │ │ ├── info.go │ │ ├── info_test.go │ │ ├── linux_info.go │ │ ├── linux_info_test.go │ │ ├── mock_creator.go │ │ ├── windows_info.go │ │ └── windows_info_test.go │ ├── services │ │ ├── services.go │ │ ├── services_test.go │ │ └── test │ │ │ └── test.go │ └── windows │ │ ├── version.go │ │ └── version_test.go ├── converter.go ├── converter_test.go ├── dns │ ├── test │ │ └── test.go │ ├── utils.go │ └── utils_test.go ├── docker │ ├── auth │ │ ├── auth.go │ │ ├── auth_test.go │ │ └── testdata │ │ │ ├── docker-credential-bin.sh │ │ │ └── docker-credential-windows.cmd │ ├── client.go │ ├── credentials.go │ ├── errors │ │ └── errors.go │ ├── machine.go │ ├── machine_command.go │ ├── machine_command_test.go │ ├── mock_Client.go │ ├── mock_Machine.go │ ├── official_docker_client.go │ ├── official_docker_client_test.go │ ├── sockets.go │ └── test │ │ └── error.go ├── fatal_panic.go ├── featureflags │ ├── flags.go │ └── flags_test.go ├── gitlab_ci_yaml_parser │ ├── data_bag.go │ ├── data_bag_test.go │ ├── parser.go │ └── parser_test.go ├── home_dir.go ├── integration_tests.go ├── limitwriter │ ├── limit_writer.go │ └── limit_writer_test.go ├── mock_RawLogger.go ├── parallels │ └── control.go ├── path.go ├── path │ ├── unix_path.go │ ├── unix_path_test.go │ ├── windows_path.go │ └── windows_path_test.go ├── path_test.go ├── process │ ├── commander.go │ ├── commander_unix_test.go │ ├── group_unix.go │ ├── group_unix_test.go │ ├── group_windows.go │ ├── group_windows_test.go │ ├── helpers_killer_test.go │ ├── killer.go │ ├── killer_integration_test.go │ ├── killer_test.go │ ├── killer_unix.go │ ├── killer_unix_integration_test.go │ ├── killer_unix_test.go │ ├── killer_windows.go │ ├── killer_windows_integration_test.go │ ├── logger.go │ ├── mock_Commander.go │ ├── mock_KillWaiter.go │ ├── mock_Logger.go │ ├── mock_killer.go │ └── testdata │ │ └── sleep │ │ └── main.go ├── prometheus │ ├── failures_collector.go │ ├── failures_collector_test.go │ ├── log_hook.go │ └── log_hook_test.go ├── random_uuid.go ├── retry │ ├── mock_Retryable.go │ ├── retry.go │ └── retry_test.go ├── secrets │ ├── errors.go │ ├── errors_test.go │ └── resolvers │ │ └── vault │ │ ├── resolver.go │ │ └── resolver_test.go ├── sentry │ ├── log_hook.go │ └── log_hook_test.go ├── service │ ├── mock_stopStarter.go │ ├── scripts.go │ ├── service_factory.go │ ├── simple.go │ └── simple_test.go ├── shell_escape.go ├── shell_escape_legacy.go ├── shell_escape_legacy_test.go ├── shell_escape_test.go ├── shorten_token.go ├── shorten_token_test.go ├── ssh │ ├── consts.go │ ├── ssh_command.go │ ├── ssh_command_test.go │ ├── ssh_config.go │ └── stub_ssh_server.go ├── test │ └── helpers.go ├── timeperiod │ ├── period.go │ └── period_test.go ├── tls │ ├── ca_chain │ │ ├── builder.go │ │ ├── builder_test.go │ │ ├── helpers.go │ │ ├── helpers_test.go │ │ ├── mock_Builder.go │ │ ├── mock_fetcher.go │ │ ├── mock_resolver.go │ │ ├── resolver.go │ │ ├── resolver_chain.go │ │ ├── resolver_chain_test.go │ │ ├── resolver_url.go │ │ ├── resolver_url_test.go │ │ ├── resolver_verify.go │ │ └── resolver_verify_test.go │ └── consts.go ├── toml_test.go ├── trace │ ├── buffer.go │ ├── buffer_fd0_test.go │ ├── buffer_fuzz.go │ ├── buffer_test.go │ ├── mask.go │ ├── mask_test.go │ └── testdata │ │ └── corpus │ │ ├── ipsum │ │ ├── log-1 │ │ ├── log-2 │ │ ├── log-3 │ │ ├── log-4 │ │ ├── log-5 │ │ └── small-random ├── url │ ├── clean_url.go │ ├── clean_url_test.go │ ├── scrub_secrets.go │ └── scrub_secrets_test.go ├── vault │ ├── auth.go │ ├── auth_methods │ │ ├── data.go │ │ ├── data_test.go │ │ ├── jwt │ │ │ ├── auth.go │ │ │ └── auth_test.go │ │ ├── registry.go │ │ └── registry_test.go │ ├── client.go │ ├── client_test.go │ ├── internal │ │ └── registry │ │ │ ├── mock_Registry.go │ │ │ ├── registry.go │ │ │ └── registry_test.go │ ├── mock_AuthMethod.go │ ├── mock_Client.go │ ├── mock_Result.go │ ├── mock_SecretEngine.go │ ├── mock_apiClient.go │ ├── mock_apiClientLogical.go │ ├── mock_apiClientSys.go │ ├── result.go │ ├── result_test.go │ ├── secret_engine.go │ ├── secret_engines │ │ ├── kv_v1 │ │ │ ├── engine.go │ │ │ └── engine_test.go │ │ ├── kv_v2 │ │ │ ├── engine.go │ │ │ └── engine_test.go │ │ ├── operations.go │ │ ├── operations_test.go │ │ ├── registry.go │ │ └── registry_test.go │ ├── service │ │ ├── mock_Auth.go │ │ ├── mock_Engine.go │ │ ├── mock_Secret.go │ │ ├── mock_Vault.go │ │ ├── vault.go │ │ └── vault_test.go │ ├── utils.go │ └── utils_test.go ├── virtualbox │ ├── control.go │ ├── control_test.go │ └── control_windows.go └── warning_panic.go ├── log ├── configuration.go ├── configuration_test.go ├── dump_unix.go ├── dump_unix_test.go ├── dump_windows.go ├── mock_systemLogger.go ├── mock_systemService.go ├── runner_formatter.go ├── runner_formatter_test.go ├── secrets_cleanup.go ├── secrets_cleanup_test.go ├── system_logger.go ├── system_logger_test.go └── test │ ├── hook.go │ └── hook_test.go ├── main.go ├── main_test.go ├── network ├── client.go ├── client_test.go ├── config.go ├── gitlab.go ├── gitlab_test.go ├── mock_requester.go ├── patch_response.go ├── ratelimit_requester.go ├── ratelimit_requester_test.go ├── remote_job_state_response.go ├── remote_job_state_response_test.go ├── requester.go ├── trace.go └── trace_test.go ├── packaging ├── root │ └── usr │ │ └── share │ │ └── gitlab-runner │ │ ├── clear-docker-cache │ │ ├── post-install │ │ └── pre-remove └── scripts │ ├── postinst.deb │ ├── postinst.rpm │ ├── prerm.deb │ └── prerm.rpm ├── referees ├── metrics.go ├── metrics_test.go ├── mock_MetricsExecutor.go ├── mock_Referee.go ├── mock_prometheusAPI.go ├── mock_prometheusValue.go ├── prometheus_api.go ├── referees.go └── referees_test.go ├── scripts ├── check-test-directives │ └── main.go ├── check_race_conditions ├── check_test_directives ├── check_unexpected_test_failures ├── check_windows_failures ├── check_windows_test_ignore_list ├── envs │ ├── README.md │ ├── allowlist_common.env │ ├── allowlist_unix.env │ └── allowlist_windows.env ├── go_test_no_env ├── go_test_no_env.ps1 ├── go_test_with_coverage_report ├── lint-docs ├── pull-images-for-tests │ └── main.go ├── security-harness ├── update-feature-flags-docs │ └── main.go └── vagrant │ └── provision │ ├── base.ps1 │ ├── enable_developer_mode.ps1 │ ├── enable_sshd.ps1 │ ├── install_PSWindowsUpdate.ps1 │ └── windows_update.ps1 ├── session ├── proxy │ ├── mock_Pooler.go │ ├── mock_Requester.go │ ├── proxy.go │ └── proxy_test.go ├── server.go ├── server_test.go ├── session.go ├── session_test.go └── terminal │ ├── mock_Conn.go │ ├── mock_InteractiveTerminal.go │ └── terminal.go ├── shells ├── abstract.go ├── abstract_test.go ├── bash.go ├── bash_test.go ├── bash_trap.go ├── cmd.go ├── cmd_test.go ├── consts.go ├── mock_ShellWriter.go ├── powershell.go ├── powershell_test.go ├── shell_writer.go ├── shell_writer_integration_test.go ├── shell_writer_test.go ├── shellstest │ └── utils.go ├── trap_command_exit_status.go └── trap_command_exit_status_test.go ├── tests ├── dockerfiles │ ├── alpine-entrypoint │ │ ├── Dockerfile │ │ ├── Dockerfile.stderr │ │ ├── entrypoint-stderr.sh │ │ └── entrypoint.sh │ ├── alpine-id-overflow │ │ └── Dockerfile │ └── alpine-no-root │ │ └── Dockerfile ├── test_installation.sh ├── test_script.sh └── ubuntu │ ├── Makefile │ └── Vagrantfile └── tools.go /.codacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - "README.md" 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | builds/ 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | indent_style = space 10 | charset = utf-8 11 | 12 | [*.{md,markdown}] 13 | trim_trailing_whitespace = false 14 | 15 | [*.{go,mod}] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | export BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-go 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sh text eol=lf 2 | ci/version text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | config.toml 3 | config.toml.lock 4 | .project 5 | out 6 | builds/ 7 | vendor/ 8 | commands/helpers/archive.zip 9 | dockerfiles/**/checksums-* 10 | dockerfiles/runner/*/install-deps 11 | dockerfiles/runner/alpine/gitlab-runner-linux-* 12 | dockerfiles/runner/ubuntu/gitlab-runner_*.deb 13 | dockerfiles/runner-helper/binaries/ 14 | .DS_Store 15 | .idea/ 16 | tests/ubuntu/.vagrant 17 | artifacts 18 | tmp/gitlab-test 19 | /.tmp/ 20 | tmp 21 | 22 | # Ignore all editorconfig files except the root one 23 | .editorconfig 24 | !/.editorconfig 25 | 26 | testsdefinitions.txt 27 | /.testoutput/ 28 | /.cover/ 29 | 30 | /.vagrant/ 31 | 32 | ci/.test-failures.servercore*.txt.updated 33 | 34 | # Ignore Visual Studio Code internals 35 | /.vscode 36 | /debug 37 | debug.test 38 | 39 | # Ignore the generated binary 40 | /gitlab-runner* 41 | 42 | .dccache -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - prepare 3 | - fips helper binaries 4 | - prebuild 5 | - test 6 | - coverage 7 | - build 8 | - package 9 | - release 10 | - postrelease 11 | - docs 12 | 13 | include: 14 | - local: /.gitlab/ci/_common.gitlab-ci.yml 15 | - local: /.gitlab/ci/_rules.gitlab-ci.yml 16 | - local: /.gitlab/ci/prepare.gitlab-ci.yml 17 | - local: /.gitlab/ci/fips-helper-binaries.gitlab-ci.yml 18 | - local: /.gitlab/ci/prebuild.gitlab-ci.yml 19 | - local: /.gitlab/ci/test.gitlab-ci.yml 20 | - local: /.gitlab/ci/coverage.gitlab-ci.yml 21 | - local: /.gitlab/ci/build.gitlab-ci.yml 22 | - local: /.gitlab/ci/package.gitlab-ci.yml 23 | - local: /.gitlab/ci/release.gitlab-ci.yml 24 | - local: /.gitlab/ci/postrelease.gitlab-ci.yml 25 | - local: /.gitlab/ci/docs.gitlab-ci.yml 26 | -------------------------------------------------------------------------------- /.gitlab/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # When adding a group as a code owner, make sure to invite the group to the 2 | # project here: https://gitlab.com/gitlab-org/gitlab-runner/-/project_members 3 | # As described in https://docs.gitlab.com/ee/user/project/code_owners.html 4 | 5 | * @gitlab-com/runner-maintainers 6 | .editorconfig @gitlab-com/runner-group @gitlab-com/runner-maintainers 7 | .gitattributes @gitlab-com/runner-group @gitlab-com/runner-maintainers 8 | .gitignore @gitlab-com/runner-group @gitlab-com/runner-maintainers 9 | 10 | [Documentation] 11 | .markdownlint.yml @gitlab-com/runner-docs-maintainers 12 | /docs/ @gitlab-com/runner-docs-maintainers 13 | -------------------------------------------------------------------------------- /.gitlab/ci/fips-helper-binaries.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | fips helper binaries: 2 | extends: 3 | - .rules:merge_request_pipelines:no_docs 4 | - .go-cache 5 | stage: fips helper binaries 6 | image: $GO_FIPS_IMAGE 7 | needs: 8 | - job: 'prepare go fips' 9 | optional: true 10 | script: 11 | - make out/binaries/gitlab-runner-helper/gitlab-runner-helper-fips GOOS=$GOOS GOARCH=$GOARCH 12 | artifacts: 13 | paths: 14 | - out/binaries/gitlab-runner-helper/gitlab-runner-helper.* 15 | expire_in: 7d 16 | parallel: 17 | matrix: 18 | - GOOS: linux 19 | GOARCH: amd64 20 | -------------------------------------------------------------------------------- /.gitlab/issue_templates/Default.md: -------------------------------------------------------------------------------- 1 | For GitLab.com specific CI/CD problems, please raise an issue in https://gitlab.com/gitlab-com/support-forum/issues. GitLab Runner issue tracker is reserved for _GitLab Runner the software_ related problems. 2 | 3 | Before raising an issue to the GitLab Runner issue tracker, please read through our guide for finding help to determine the best place to post: 4 | 5 | * https://about.gitlab.com/getting-help/ 6 | 7 | If you feel that your issue can be categorized as an existing bug or a feature proposal, please use one of the issue templates provided and include as much information as possible. 8 | 9 | Thank you for helping to make GitLab Runner a better product. 10 | -------------------------------------------------------------------------------- /.gitlab/issue_templates/Feature Proposal.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 6 | 7 | ## Proposal 8 | 9 | ## Links to related issues and merge requests / references 10 | 11 | 14 | 15 | /label ~feature 16 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/Default.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## What does this MR do? 7 | 8 | ## Why was this MR needed? 9 | 10 | ## What's the best way to test this MR? 11 | 12 | ## What are the relevant issue numbers? 13 | -------------------------------------------------------------------------------- /.gitlab/route-map.yml: -------------------------------------------------------------------------------- 1 | # Documentation 2 | - source: /docs/(.+?)\.md/ 3 | public: '\1.html' 4 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - id: darwin-386 3 | binary: anka-gitlab-runner-darwin-386 4 | goos: 5 | - darwin 6 | goarch: 7 | - "386" 8 | - id: darwin-amd64 9 | binary: anka-gitlab-runner-darwin-amd64 10 | goos: 11 | - darwin 12 | goarch: 13 | - amd64 14 | - id: linux-386 15 | binary: anka-gitlab-runner-linux-386 16 | goos: 17 | - linux 18 | goarch: 19 | - "386" 20 | - id: linux-amd64 21 | binary: anka-gitlab-runner-linux-amd64 22 | goos: 23 | - linux 24 | goarch: 25 | - amd64 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | golang 1.17.7 -------------------------------------------------------------------------------- /.vale.ini: -------------------------------------------------------------------------------- 1 | # Vale configuration file. 2 | # 3 | # For more information, see https://errata-ai.gitbook.io/vale/getting-started/configuration. 4 | 5 | StylesPath = docs/.vale 6 | MinAlertLevel = suggestion 7 | 8 | [*.md] 9 | BasedOnStyles = gitlab 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "golang.go", 4 | "766b.go-outliner", 5 | "bungcip.better-toml", 6 | "davidanson.vscode-markdownlint", 7 | "romanpeshkov.go-test-outline", 8 | "errata-ai.vale-server", 9 | "timonwong.shellcheck", 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/Jenkinsfile -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gitlab-runner run-single -addr=":$PORT" -builds-dir="/tmp" 2 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 14.10.1/1.6.1 2 | -------------------------------------------------------------------------------- /boring/boring.go: -------------------------------------------------------------------------------- 1 | //go:build boringcrypto 2 | // +build boringcrypto 3 | 4 | package boring 5 | 6 | import "fmt" 7 | 8 | func CheckBoring() { 9 | fmt.Println("FIPS mode enabled. Using BoringSSL.") 10 | } 11 | -------------------------------------------------------------------------------- /boring/notboring.go: -------------------------------------------------------------------------------- 1 | //go:build !boringcrypto 2 | // +build !boringcrypto 3 | 4 | package boring 5 | 6 | func CheckBoring() { 7 | } 8 | -------------------------------------------------------------------------------- /build-and-tar.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | VERSION=$(cat $ROOT_DIR/VERSION | cut -d/ -f2) 5 | cd $ROOT_DIR 6 | # Build binaries 7 | echo "Building binaries..." 8 | make runner-bin 9 | for arch in linux-amd64 linux-386; do # notarization handles the archive for darwin 10 | mkdir -p $ROOT_DIR/out/archived_binaries 11 | cp $ROOT_DIR/out/binaries/anka-gitlab-runner-$arch $ROOT_DIR/out/archived_binaries/ 12 | pushd $ROOT_DIR/out/archived_binaries 13 | echo "Creating tar.gz for $arch binary..." 14 | rm -f anka-gitlab-runner-v$VERSION-$arch.tar.gz 15 | tar -czvf anka-gitlab-runner-v$VERSION-$arch.tar.gz anka-gitlab-runner-$arch 16 | rm -f anka-gitlab-runner-$arch 17 | popd 18 | done 19 | -------------------------------------------------------------------------------- /cache/gcs/mock_credentialsResolver.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package gcs 4 | 5 | import ( 6 | mock "github.com/stretchr/testify/mock" 7 | common "gitlab.com/gitlab-org/gitlab-runner/common" 8 | ) 9 | 10 | // mockCredentialsResolver is an autogenerated mock type for the credentialsResolver type 11 | type mockCredentialsResolver struct { 12 | mock.Mock 13 | } 14 | 15 | // Credentials provides a mock function with given fields: 16 | func (_m *mockCredentialsResolver) Credentials() *common.CacheGCSCredentials { 17 | ret := _m.Called() 18 | 19 | var r0 *common.CacheGCSCredentials 20 | if rf, ok := ret.Get(0).(func() *common.CacheGCSCredentials); ok { 21 | r0 = rf() 22 | } else { 23 | if ret.Get(0) != nil { 24 | r0 = ret.Get(0).(*common.CacheGCSCredentials) 25 | } 26 | } 27 | 28 | return r0 29 | } 30 | 31 | // Resolve provides a mock function with given fields: 32 | func (_m *mockCredentialsResolver) Resolve() error { 33 | ret := _m.Called() 34 | 35 | var r0 error 36 | if rf, ok := ret.Get(0).(func() error); ok { 37 | r0 = rf() 38 | } else { 39 | r0 = ret.Error(0) 40 | } 41 | 42 | return r0 43 | } 44 | -------------------------------------------------------------------------------- /cache/mock_CredentialsAdapter.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package cache 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockCredentialsAdapter is an autogenerated mock type for the CredentialsAdapter type 8 | type MockCredentialsAdapter struct { 9 | mock.Mock 10 | } 11 | 12 | // GetCredentials provides a mock function with given fields: 13 | func (_m *MockCredentialsAdapter) GetCredentials() map[string]string { 14 | ret := _m.Called() 15 | 16 | var r0 map[string]string 17 | if rf, ok := ret.Get(0).(func() map[string]string); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(map[string]string) 22 | } 23 | } 24 | 25 | return r0 26 | } 27 | -------------------------------------------------------------------------------- /cache/s3/bucket_location_tripper.go: -------------------------------------------------------------------------------- 1 | package s3 2 | 3 | import ( 4 | "bytes" 5 | "encoding/xml" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | type bucketLocationTripper struct { 11 | bucketLocation string 12 | } 13 | 14 | // The Minio Golang library always attempts to query the bucket location and 15 | // currently has no way of statically setting that value. To avoid that 16 | // lookup, the Runner cache uses the library only to generate the URLs, 17 | // forgoing the library's API for uploading and downloading files. The custom 18 | // Roundtripper stubs out any network requests that would normally be made via 19 | // the library. 20 | func (b *bucketLocationTripper) RoundTrip(req *http.Request) (res *http.Response, err error) { 21 | var buffer bytes.Buffer 22 | err = xml.NewEncoder(&buffer).Encode(b.bucketLocation) 23 | if err != nil { 24 | return 25 | } 26 | res = &http.Response{ 27 | StatusCode: http.StatusOK, 28 | Body: ioutil.NopCloser(&buffer), 29 | } 30 | return 31 | } 32 | 33 | func (b *bucketLocationTripper) CancelRequest(req *http.Request) { 34 | // Do nothing 35 | } 36 | -------------------------------------------------------------------------------- /ci/.colors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Source this file to get color definitions for your script output. 4 | # e.g. `echo -e "${RED}message in red${RST}"` 5 | 6 | export YLW='\033[1;33m' 7 | export RED='\033[0;31m' 8 | export GRN='\033[0;32m' 9 | export RST='\033[0m' 10 | -------------------------------------------------------------------------------- /ci/.test-failures.servercore1809.txt: -------------------------------------------------------------------------------- 1 | TestBuildOnCustomDirectory 2 | TestBuildOnCustomDirectory/powershell 3 | TestBuildOnCustomDirectory/powershell/custom_directory_defined 4 | TestBuildWithDebugTrace 5 | TestBuildWithDebugTrace/cmd 6 | TestList 7 | TestList/machines_directory_is_invalid 8 | TestMachineIdleLimits 9 | TestMachineProviderDeadInterval 10 | TestNewDockerMachineCommandCtx 11 | TestNewDockerMachineCommandCtx/MACHINE_BUGSNAG_API_TOKEN_is_defined_by_the_user 12 | TestNewDockerMachineCommandCtx/MACHINE_BUGSNAG_API_TOKEN_is_not_defined_by_the_user 13 | TestPrepare 14 | -------------------------------------------------------------------------------- /ci/.test-failures.servercore2004.txt: -------------------------------------------------------------------------------- 1 | TestBuildOnCustomDirectory 2 | TestBuildOnCustomDirectory/powershell 3 | TestBuildOnCustomDirectory/powershell/custom_directory_defined 4 | TestBuildOnCustomDirectory/pwsh 5 | TestBuildOnCustomDirectory/pwsh/custom_directory_defined 6 | TestBuildWithDebugTrace 7 | TestBuildWithDebugTrace/cmd 8 | TestList 9 | TestList/machines_directory_is_invalid 10 | TestMachineProviderDeadInterval 11 | TestNewDockerMachineCommandCtx 12 | TestNewDockerMachineCommandCtx/MACHINE_BUGSNAG_API_TOKEN_is_defined_by_the_user 13 | TestNewDockerMachineCommandCtx/MACHINE_BUGSNAG_API_TOKEN_is_not_defined_by_the_user 14 | TestPrepare 15 | -------------------------------------------------------------------------------- /ci/.test-failures.servercore20H2.txt: -------------------------------------------------------------------------------- 1 | TestBuildOnCustomDirectory 2 | TestBuildOnCustomDirectory/powershell 3 | TestBuildOnCustomDirectory/powershell/custom_directory_defined 4 | TestBuildOnCustomDirectory/pwsh 5 | TestBuildOnCustomDirectory/pwsh/custom_directory_defined 6 | TestBuildWithDebugTrace 7 | TestBuildWithDebugTrace/cmd 8 | TestList 9 | TestList/machines_directory_is_invalid 10 | TestMachineProviderDeadInterval 11 | TestNewDockerMachineCommandCtx 12 | TestNewDockerMachineCommandCtx/MACHINE_BUGSNAG_API_TOKEN_is_defined_by_the_user 13 | TestNewDockerMachineCommandCtx/MACHINE_BUGSNAG_API_TOKEN_is_not_defined_by_the_user 14 | TestPrepare 15 | TestDockerServiceHealthcheck 16 | TestDockerServiceHealthcheck/successful_service_(FF_NETWORK_PER_BUILD=true) 17 | TestDockerServiceHealthcheck/failed_service_(FF_NETWORK_PER_BUILD=true) 18 | -------------------------------------------------------------------------------- /ci/build_ci_image: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | source "ci/_build_ci_image_common" 6 | 7 | build() { 8 | echo "Building image: ${BUILD_IMAGE}" 9 | docker build \ 10 | --cache-from "${BUILD_IMAGE}" \ 11 | --build-arg DOCKER_VERSION="${DOCKER_VERSION}" \ 12 | --build-arg BUILDX_VERSION="${BUILDX_VERSION}" \ 13 | --build-arg PWSH_VERSION="${PWSH_VERSION}" \ 14 | --build-arg GIT_LFS_VERSION="${GIT_LFS_VERSION}" \ 15 | --build-arg GIT_LFS_AMD64_CHECKSUM="${GIT_LFS_LINUX_AMD64_CHECKSUM}" \ 16 | --build-arg KUBECTL_VERSION="${KUBECTL_VERSION}" \ 17 | --build-arg AWS_CLI_VERSION="${AWS_CLI_VERSION}" \ 18 | -t "${BUILD_IMAGE}" \ 19 | -f "${BUILD_DOCKERFILE}" \ 20 | "${GIT_ROOT}" 21 | } 22 | 23 | login 24 | pull 25 | build 26 | push 27 | logout 28 | scan 29 | -------------------------------------------------------------------------------- /ci/build_go_fips_image: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | source "ci/_build_ci_image_common" 6 | 7 | build() { 8 | echo "Building Go FIPS image: ${BUILD_IMAGE}" 9 | docker build \ 10 | --cache-from "${BUILD_IMAGE}" \ 11 | --build-arg GO_VERSION="${GO_VERSION}" \ 12 | --build-arg UBI_VERSION="${UBI_VERSION}" \ 13 | -t "${BUILD_IMAGE}" \ 14 | -f "${BUILD_DOCKERFILE}" \ 15 | "${GIT_ROOT}" 16 | } 17 | 18 | login 19 | pull 20 | build 21 | push 22 | logout 23 | scan 24 | -------------------------------------------------------------------------------- /ci/push_packagecloud: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | distFlavor=${DIST_FLAVOR:-""} 4 | 5 | packageCloudURL=${1} 6 | packageCloudRepo=${2} 7 | packageType=${3} 8 | shift 3 9 | 10 | push_packagecloud() { 11 | local dist="${1}" 12 | 13 | echo "====================" 14 | echo "${dist}" 15 | echo "====================" 16 | 17 | package_cloud push --verbose --url "${packageCloudURL}" "${packageCloudRepo}/${dist}" out/${packageType}/*.${packageType} 18 | } 19 | 20 | # shellcheck disable=SC2068 21 | for dist in ${@}; do 22 | if [[ -z "${distFlavor}" ]] || [[ "${dist}" =~ ${distFlavor} ]]; then 23 | push_packagecloud "${dist}" 24 | fi 25 | done 26 | -------------------------------------------------------------------------------- /ci/release_packagecloud: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | target=$(echo "${1}" | cut -f 1 -d ' ') 6 | case "${target}" in 7 | bleeding) 8 | make packagecloud PACKAGE_CLOUD="runner/unstable" \ 9 | DEB_PLATFORMS="debian/stretch debian/buster debian/bullseye ubuntu/xenial ubuntu/bionic ubuntu/focal" \ 10 | RPM_PLATFORMS="el/7 el/8 fedora/32 fedora/33 fedora/34 amazon/2" 11 | ;; 12 | stable) 13 | make packagecloud 14 | ;; 15 | *) 16 | echo "Unknown packagecloud upload target: '${target}'" 17 | exit 1 18 | ;; 19 | esac 20 | -------------------------------------------------------------------------------- /ci/release_s3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | refName=${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:-$CI_COMMIT_REF_NAME} 6 | 7 | make s3-upload "S3_UPLOAD_PATH=${refName}" 8 | if [[ -n "${IS_LATEST}" ]]; then 9 | make s3-upload "S3_UPLOAD_PATH=latest" 10 | fi 11 | -------------------------------------------------------------------------------- /ci/touch_make_dependencies: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -d out ]; then 4 | find out -type f -exec sh -c ' 5 | echo "touching $1" 6 | touch "$1" 7 | ' sh {} \; 8 | fi 9 | -------------------------------------------------------------------------------- /ci/upstream_commit_ref: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -n "$CI_COMMIT_TAG" ]]; then 4 | echo "$CI_COMMIT_TAG" 5 | elif [[ -n "$CI_MERGE_REQUEST_IID" ]]; then 6 | echo "refs/merge-requests/$CI_MERGE_REQUEST_IID/merge" 7 | else 8 | echo "${CI_DEFAULT_BRANCH:-main}" 9 | fi 10 | -------------------------------------------------------------------------------- /ci/version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | if git version | grep 'git version 1' >/dev/null; then 6 | # Old version of git. This will cause errors in the git1.8-test CI job, so if we're executing in CI, let's just 7 | # ignore it as the job does not require this info 8 | [ -n "${CI}" ] && exit 0 9 | fi 10 | 11 | version=$(cat VERSION || echo dev | sed -e 's/^v//g') 12 | exact_tag=$(git describe --exact-match 2>/dev/null | sed -e 's/^v//g' || echo "") 13 | 14 | if echo "${exact_tag}" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+$"; then 15 | echo "$exact_tag" 16 | exit 0 17 | fi 18 | 19 | if echo "${exact_tag}" | grep -qE "^[0-9]+\.[0-9]+\.[0-9]+-rc[0-9]+$"; then 20 | echo "$exact_tag" 21 | exit 0 22 | fi 23 | 24 | last_tag=$(git describe --abbrev=0 --exclude='*-rc*') 25 | # commits=$(git rev-list --count "${last_tag}..HEAD") 26 | # revision=$(git rev-parse --short=8 HEAD || echo unknown) 27 | 28 | echo "${version}" 29 | -------------------------------------------------------------------------------- /commands/config_unix.go: -------------------------------------------------------------------------------- 1 | //go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris 2 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 3 | 4 | package commands 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | 10 | "gitlab.com/gitlab-org/gitlab-runner/helpers" 11 | ) 12 | 13 | var ROOTCONFIGDIR = "/etc/gitlab-runner" 14 | 15 | func getDefaultConfigDirectory() string { 16 | if os.Getuid() == 0 { 17 | return ROOTCONFIGDIR 18 | } else if homeDir := helpers.GetHomeDir(); homeDir != "" { 19 | return filepath.Join(homeDir, ".gitlab-runner") 20 | } else if currentDir := helpers.GetCurrentWorkingDirectory(); currentDir != "" { 21 | return currentDir 22 | } 23 | panic("Cannot get default config file location") 24 | } 25 | -------------------------------------------------------------------------------- /commands/config_windows.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "gitlab.com/gitlab-org/gitlab-runner/helpers" 5 | ) 6 | 7 | func getDefaultConfigDirectory() string { 8 | if currentDir := helpers.GetCurrentWorkingDirectory(); currentDir != "" { 9 | return currentDir 10 | } 11 | 12 | panic("Cannot get default config file location") 13 | } 14 | -------------------------------------------------------------------------------- /commands/constants.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | const ( 4 | osTypeLinux = "linux" 5 | osTypeDarwin = "darwin" 6 | osTypeWindows = "windows" 7 | ) 8 | -------------------------------------------------------------------------------- /commands/helpers/archive/fastzip/zip_fastzip_extractor.go: -------------------------------------------------------------------------------- 1 | package fastzip 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | "github.com/saracen/fastzip" 8 | 9 | "gitlab.com/gitlab-org/gitlab-runner/commands/helpers/archive" 10 | ) 11 | 12 | // extractor is a zip stream extractor. 13 | type extractor struct { 14 | r io.ReaderAt 15 | size int64 16 | dir string 17 | } 18 | 19 | // NewExtractor returns a new Zip Extractor. 20 | func NewExtractor(r io.ReaderAt, size int64, dir string) (archive.Extractor, error) { 21 | return &extractor{r: r, size: size, dir: dir}, nil 22 | } 23 | 24 | // Extract extracts files from the reader to the directory passed to 25 | // NewExtractor. 26 | func (e *extractor) Extract(ctx context.Context) error { 27 | extractor, err := fastzip.NewExtractorFromReader(e.r, e.size, e.dir) 28 | if err != nil { 29 | return err 30 | } 31 | defer extractor.Close() 32 | 33 | return extractor.Extract(ctx) 34 | } 35 | -------------------------------------------------------------------------------- /commands/helpers/archive/gziplegacy/gzip_legacy_archiver.go: -------------------------------------------------------------------------------- 1 | package gziplegacy 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "os" 7 | "sort" 8 | 9 | "gitlab.com/gitlab-org/gitlab-runner/commands/helpers/archive" 10 | "gitlab.com/gitlab-org/gitlab-runner/helpers/archives" 11 | ) 12 | 13 | func init() { 14 | archive.Register(archive.Gzip, NewArchiver, nil) 15 | } 16 | 17 | // archiver is a gzip stream archiver. 18 | type archiver struct { 19 | w io.Writer 20 | dir string 21 | } 22 | 23 | // NewArchiver returns a new Gzip Archiver. 24 | func NewArchiver(w io.Writer, dir string, level archive.CompressionLevel) (archive.Archiver, error) { 25 | return &archiver{w: w, dir: dir}, nil 26 | } 27 | 28 | // Archive archives all files as new gzip streams. 29 | func (a *archiver) Archive(ctx context.Context, files map[string]os.FileInfo) error { 30 | sorted := make([]string, 0, len(files)) 31 | for filename := range files { 32 | sorted = append(sorted, filename) 33 | } 34 | sort.Strings(sorted) 35 | 36 | return archives.CreateGzipArchive(a.w, sorted) 37 | } 38 | -------------------------------------------------------------------------------- /commands/helpers/archive/mock_Archiver.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package archive 4 | 5 | import ( 6 | context "context" 7 | fs "io/fs" 8 | 9 | mock "github.com/stretchr/testify/mock" 10 | ) 11 | 12 | // MockArchiver is an autogenerated mock type for the Archiver type 13 | type MockArchiver struct { 14 | mock.Mock 15 | } 16 | 17 | // Archive provides a mock function with given fields: ctx, files 18 | func (_m *MockArchiver) Archive(ctx context.Context, files map[string]fs.FileInfo) error { 19 | ret := _m.Called(ctx, files) 20 | 21 | var r0 error 22 | if rf, ok := ret.Get(0).(func(context.Context, map[string]fs.FileInfo) error); ok { 23 | r0 = rf(ctx, files) 24 | } else { 25 | r0 = ret.Error(0) 26 | } 27 | 28 | return r0 29 | } 30 | -------------------------------------------------------------------------------- /commands/helpers/archive/mock_Extractor.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package archive 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockExtractor is an autogenerated mock type for the Extractor type 12 | type MockExtractor struct { 13 | mock.Mock 14 | } 15 | 16 | // Extract provides a mock function with given fields: ctx 17 | func (_m *MockExtractor) Extract(ctx context.Context) error { 18 | ret := _m.Called(ctx) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(context.Context) error); ok { 22 | r0 = rf(ctx) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | -------------------------------------------------------------------------------- /commands/helpers/archive/ziplegacy/zip_legacy_archiver.go: -------------------------------------------------------------------------------- 1 | package ziplegacy 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "os" 7 | "sort" 8 | 9 | "gitlab.com/gitlab-org/gitlab-runner/commands/helpers/archive" 10 | "gitlab.com/gitlab-org/gitlab-runner/helpers/archives" 11 | ) 12 | 13 | func init() { 14 | archive.Register(archive.Zip, NewArchiver, NewExtractor) 15 | } 16 | 17 | // archiver is a zip stream archiver. 18 | type archiver struct { 19 | w io.Writer 20 | dir string 21 | } 22 | 23 | // NewArchiver returns a new Zip Archiver. 24 | func NewArchiver(w io.Writer, dir string, level archive.CompressionLevel) (archive.Archiver, error) { 25 | return &archiver{w: w, dir: dir}, nil 26 | } 27 | 28 | // Archive archives all files as new gzip streams. 29 | func (a *archiver) Archive(ctx context.Context, files map[string]os.FileInfo) error { 30 | sorted := make([]string, 0, len(files)) 31 | for filename := range files { 32 | sorted = append(sorted, filename) 33 | } 34 | sort.Strings(sorted) 35 | 36 | return archives.CreateZipArchive(a.w, sorted) 37 | } 38 | -------------------------------------------------------------------------------- /commands/helpers/archive/ziplegacy/zip_legacy_extractor.go: -------------------------------------------------------------------------------- 1 | package ziplegacy 2 | 3 | import ( 4 | "archive/zip" 5 | "context" 6 | "io" 7 | 8 | "gitlab.com/gitlab-org/gitlab-runner/commands/helpers/archive" 9 | "gitlab.com/gitlab-org/gitlab-runner/helpers/archives" 10 | ) 11 | 12 | // extractor is a zip stream extractor. 13 | type extractor struct { 14 | r io.ReaderAt 15 | size int64 16 | dir string 17 | } 18 | 19 | // NewExtractor returns a new Zip Extractor. 20 | func NewExtractor(r io.ReaderAt, size int64, dir string) (archive.Extractor, error) { 21 | return &extractor{r: r, size: size, dir: dir}, nil 22 | } 23 | 24 | // Extract extracts files from the reader to the directory passed to 25 | // NewZipExtractor. 26 | func (e *extractor) Extract(ctx context.Context) error { 27 | zr, err := zip.NewReader(e.r, e.size) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | return archives.ExtractZipArchive(zr) 33 | } 34 | -------------------------------------------------------------------------------- /commands/helpers/cache_client.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "time" 7 | 8 | "gitlab.com/gitlab-org/gitlab-runner/common" 9 | ) 10 | 11 | type CacheClient struct { 12 | http.Client 13 | } 14 | 15 | func (c *CacheClient) prepareClient(timeout int) { 16 | if timeout > 0 { 17 | c.Timeout = time.Duration(timeout) * time.Minute 18 | } else { 19 | c.Timeout = time.Duration(common.DefaultCacheRequestTimeout) * time.Minute 20 | } 21 | } 22 | 23 | func (c *CacheClient) prepareTransport() { 24 | c.Transport = &http.Transport{ 25 | Proxy: http.ProxyFromEnvironment, 26 | DialContext: (&net.Dialer{ 27 | Timeout: 30 * time.Second, 28 | KeepAlive: 30 * time.Second, 29 | }).DialContext, 30 | IdleConnTimeout: 90 * time.Second, 31 | TLSHandshakeTimeout: 10 * time.Second, 32 | ExpectContinueTimeout: 10 * time.Second, 33 | ResponseHeaderTimeout: 30 * time.Second, 34 | DisableCompression: true, 35 | } 36 | } 37 | 38 | func NewCacheClient(timeout int) *CacheClient { 39 | client := &CacheClient{} 40 | client.prepareClient(timeout) 41 | client.prepareTransport() 42 | 43 | return client 44 | } 45 | -------------------------------------------------------------------------------- /commands/helpers/cache_init.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/sirupsen/logrus" 7 | "github.com/urfave/cli" 8 | 9 | "gitlab.com/gitlab-org/gitlab-runner/common" 10 | ) 11 | 12 | // CacheInitCommand will take a single directory/file path and initialize it 13 | // correctly for it to be used for cache. This command tries to support spaces 14 | // in directories name by using the the flags to specify which entries you want 15 | // to initialize. 16 | type CacheInitCommand struct{} 17 | 18 | func (c *CacheInitCommand) Execute(ctx *cli.Context) { 19 | if ctx.NArg() == 0 { 20 | logrus.Fatal("No arguments passed, at least 1 path is required.") 21 | } 22 | 23 | for _, path := range ctx.Args() { 24 | err := os.Chmod(path, os.ModePerm) 25 | if err != nil { 26 | logrus.WithError(err).Error("failed to chmod path") 27 | } 28 | } 29 | } 30 | 31 | func init() { 32 | common.RegisterCommand2("cache-init", "changed permissions for cache paths (internal)", &CacheInitCommand{}) 33 | } 34 | -------------------------------------------------------------------------------- /commands/helpers/helpers_cache_archiver_test.go: -------------------------------------------------------------------------------- 1 | // Helper functions that are shared between unit tests and integration tests 2 | 3 | package helpers 4 | 5 | import ( 6 | "time" 7 | 8 | "gocloud.dev/blob" 9 | ) 10 | 11 | // NewCacheArchiverCommandForTest exposes CacheArchiverCommand with fileArchiver to integration tests 12 | func NewCacheArchiverCommandForTest(file string, fileArchiverPaths []string) CacheArchiverCommand { 13 | return CacheArchiverCommand{ 14 | File: file, 15 | fileArchiver: fileArchiver{Paths: fileArchiverPaths}, 16 | } 17 | } 18 | 19 | // SetCacheArchiverCommandMux allows integration tests to set mux 20 | func SetCacheArchiverCommandMux(cmd *CacheArchiverCommand, mux *blob.URLMux) { 21 | cmd.mux = mux 22 | } 23 | 24 | // SetCacheArchiverCommandClientTimeout allows integration tests to set the client timeout 25 | func SetCacheArchiverCommandClientTimeout(cmd *CacheArchiverCommand, timeout time.Duration) { 26 | cmd.getClient().Timeout = timeout 27 | } 28 | -------------------------------------------------------------------------------- /commands/helpers/meter/reader.go: -------------------------------------------------------------------------------- 1 | package meter 2 | 3 | import ( 4 | "io" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | type reader struct { 10 | *meter 11 | 12 | r io.ReadCloser 13 | } 14 | 15 | func NewReader(r io.ReadCloser, frequency time.Duration, fn UpdateCallback) io.ReadCloser { 16 | if frequency == 0 { 17 | return r 18 | } 19 | 20 | m := &reader{ 21 | r: r, 22 | meter: newMeter(), 23 | } 24 | 25 | m.start(frequency, fn) 26 | 27 | return m 28 | } 29 | 30 | func (m *reader) Read(p []byte) (int, error) { 31 | n, err := m.r.Read(p) 32 | atomic.AddUint64(&m.count, uint64(n)) 33 | 34 | return n, err 35 | } 36 | 37 | func (m *reader) Close() error { 38 | m.doClose() 39 | 40 | return m.r.Close() 41 | } 42 | -------------------------------------------------------------------------------- /commands/helpers/meter/writer.go: -------------------------------------------------------------------------------- 1 | package meter 2 | 3 | import ( 4 | "io" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | type writer struct { 10 | *meter 11 | 12 | w io.WriteCloser 13 | } 14 | 15 | func NewWriter(w io.WriteCloser, frequency time.Duration, fn UpdateCallback) io.WriteCloser { 16 | if frequency == 0 { 17 | return w 18 | } 19 | 20 | m := &writer{ 21 | w: w, 22 | meter: newMeter(), 23 | } 24 | 25 | m.start(frequency, fn) 26 | 27 | return m 28 | } 29 | 30 | func (m *writer) Write(p []byte) (int, error) { 31 | n, err := m.w.Write(p) 32 | atomic.AddUint64(&m.count, uint64(n)) 33 | 34 | return n, err 35 | } 36 | 37 | func (m *writer) Close() error { 38 | m.doClose() 39 | 40 | return m.w.Close() 41 | } 42 | -------------------------------------------------------------------------------- /commands/helpers/mock_logOutputWriter.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package helpers 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockLogOutputWriter is an autogenerated mock type for the logOutputWriter type 8 | type mockLogOutputWriter struct { 9 | mock.Mock 10 | } 11 | 12 | // Write provides a mock function with given fields: _a0 13 | func (_m *mockLogOutputWriter) Write(_a0 string) { 14 | _m.Called(_a0) 15 | } 16 | -------------------------------------------------------------------------------- /commands/helpers/mock_logStreamProvider.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package helpers 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockLogStreamProvider is an autogenerated mock type for the logStreamProvider type 8 | type mockLogStreamProvider struct { 9 | mock.Mock 10 | } 11 | 12 | // Open provides a mock function with given fields: 13 | func (_m *mockLogStreamProvider) Open() (readSeekCloser, error) { 14 | ret := _m.Called() 15 | 16 | var r0 readSeekCloser 17 | if rf, ok := ret.Get(0).(func() readSeekCloser); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(readSeekCloser) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func() error); ok { 27 | r1 = rf() 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | -------------------------------------------------------------------------------- /commands/helpers/testdata/test-artifacts/file-0: -------------------------------------------------------------------------------- 1 | file-0 2 | -------------------------------------------------------------------------------- /commands/helpers/testdata/test-artifacts/file-1: -------------------------------------------------------------------------------- 1 | file-1 2 | -------------------------------------------------------------------------------- /commands/helpers/testdata/test-artifacts/file-2: -------------------------------------------------------------------------------- 1 | file-2 2 | -------------------------------------------------------------------------------- /commands/helpers/testdata/test-artifacts/file-3: -------------------------------------------------------------------------------- 1 | file-3 2 | -------------------------------------------------------------------------------- /commands/helpers/testdata/test-artifacts/file-4: -------------------------------------------------------------------------------- 1 | file-4 2 | -------------------------------------------------------------------------------- /commands/list.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "github.com/urfave/cli" 6 | 7 | "gitlab.com/gitlab-org/gitlab-runner/common" 8 | ) 9 | 10 | type ListCommand struct { 11 | configOptions 12 | } 13 | 14 | func (c *ListCommand) Execute(context *cli.Context) { 15 | err := c.loadConfig() 16 | if err != nil { 17 | logrus.Warningln(err) 18 | return 19 | } 20 | 21 | logrus.WithFields(logrus.Fields{ 22 | "ConfigFile": c.ConfigFile, 23 | }).Println("Listing configured runners") 24 | 25 | for _, runner := range c.config.Runners { 26 | logrus.WithFields(logrus.Fields{ 27 | "Executor": runner.RunnerSettings.Executor, 28 | "Token": runner.RunnerCredentials.Token, 29 | "URL": runner.RunnerCredentials.URL, 30 | }).Println(runner.Name) 31 | } 32 | } 33 | 34 | func init() { 35 | common.RegisterCommand2("list", "List all configured runners", &ListCommand{}) 36 | } 37 | -------------------------------------------------------------------------------- /commands/service_darwin.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/kardianos/service" 7 | "github.com/sirupsen/logrus" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | func setupOSServiceConfig(c *cli.Context, config *service.Config) { 12 | config.Option = service.KeyValue{ 13 | "KeepAlive": true, 14 | "RunAtLoad": true, 15 | "UserService": os.Getuid() != 0, 16 | } 17 | 18 | user := c.String("user") 19 | if user == "" { 20 | return 21 | } 22 | 23 | if os.Getuid() != 0 { 24 | logrus.Fatal("The --user is not supported for non-root users") 25 | } 26 | 27 | config.Arguments = append(config.Arguments, "--user", user) 28 | } 29 | -------------------------------------------------------------------------------- /commands/service_linux.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/kardianos/service" 7 | "github.com/sirupsen/logrus" 8 | "github.com/urfave/cli" 9 | service_helpers "gitlab.com/gitlab-org/gitlab-runner/helpers/service" 10 | ) 11 | 12 | func setupOSServiceConfig(c *cli.Context, config *service.Config) { 13 | if os.Getuid() != 0 { 14 | logrus.Fatal("The --user is not supported for non-root users") 15 | } 16 | 17 | user := c.String("user") 18 | if user != "" { 19 | config.Arguments = append(config.Arguments, "--user", user) 20 | } 21 | 22 | switch service.Platform() { 23 | case "linux-systemd": 24 | config.Dependencies = []string{ 25 | "After=syslog.target network.target", 26 | } 27 | config.Option = service.KeyValue{ 28 | "Restart": "always", 29 | } 30 | case "unix-systemv": 31 | script := service_helpers.SysvScript() 32 | if script != "" { 33 | config.Option = service.KeyValue{ 34 | "SysvScript": script, 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /commands/service_portable.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !darwin && !windows 2 | // +build !linux,!darwin,!windows 3 | 4 | package commands 5 | 6 | import ( 7 | "github.com/kardianos/service" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | func setupOSServiceConfig(c *cli.Context, config *service.Config) { 12 | // not supported 13 | } 14 | -------------------------------------------------------------------------------- /commands/service_windows.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/kardianos/service" 5 | "github.com/urfave/cli" 6 | ) 7 | 8 | func setupOSServiceConfig(c *cli.Context, config *service.Config) { 9 | config.Option = service.KeyValue{ 10 | "Password": c.String("password"), 11 | } 12 | config.UserName = c.String("user") 13 | } 14 | -------------------------------------------------------------------------------- /common/buildtest/binary.go: -------------------------------------------------------------------------------- 1 | package buildtest 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "runtime" 8 | ) 9 | 10 | func MustBuildBinary(entrypoint string, binaryName string) string { 11 | if runtime.GOOS == "windows" { 12 | binaryName += ".exe" 13 | } 14 | 15 | cmd := exec.Command("go", "build", "-o", binaryName, entrypoint) 16 | cmd.Stdout = os.Stdout 17 | cmd.Stderr = os.Stderr 18 | 19 | fmt.Printf("Executing: %v\n", cmd) 20 | 21 | err := cmd.Run() 22 | if err != nil { 23 | panic("Error on executing go build for binary: " + entrypoint) 24 | } 25 | 26 | return binaryName 27 | } 28 | -------------------------------------------------------------------------------- /common/buildtest/sections.go: -------------------------------------------------------------------------------- 1 | package buildtest 2 | 3 | import ( 4 | "bytes" 5 | "regexp" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "gitlab.com/gitlab-org/gitlab-runner/common" 10 | "gitlab.com/gitlab-org/gitlab-runner/helpers/featureflags" 11 | ) 12 | 13 | func RunBuildWithSections(t *testing.T, build *common.Build) { 14 | build.Features.TraceSections = true 15 | build.Variables = append(build.Variables, common.JobVariable{ 16 | Key: featureflags.ScriptSections, 17 | Value: "true", 18 | }) 19 | 20 | buf := new(bytes.Buffer) 21 | trace := &common.Trace{Writer: buf} 22 | assert.NoError(t, RunBuildWithTrace(t, build, trace)) 23 | //nolint:lll 24 | // section_start:1627911560:section_27e4a11ba6450738\r\x1b[0K\x1b[32;1m$ echo Hello World\x1b[0;m\nHello World\n\x1b[0Ksection_end:1627911560:section_27e4a11ba6450738 25 | assert.Regexp(t, regexp.MustCompile("(?s)section_start:[0-9]+:section_script_step_[0-9]+.*Hello World.*section_end:[0-9]+:section_script_step_[0-9]"), buf.String()) 26 | } 27 | -------------------------------------------------------------------------------- /common/command.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "github.com/urfave/cli" 6 | clihelpers "gitlab.com/gitlab-org/golang-cli-helpers" 7 | ) 8 | 9 | var commands []cli.Command 10 | 11 | type Commander interface { 12 | Execute(c *cli.Context) 13 | } 14 | 15 | func RegisterCommand(command cli.Command) { 16 | logrus.Debugln("Registering", command.Name, "command...") 17 | commands = append(commands, command) 18 | } 19 | 20 | func RegisterCommand2(name, usage string, data Commander, flags ...cli.Flag) { 21 | RegisterCommand(cli.Command{ 22 | Name: name, 23 | Usage: usage, 24 | Action: data.Execute, 25 | Flags: append(flags, clihelpers.GetFlagsFromStruct(data)...), 26 | }) 27 | } 28 | 29 | func GetCommands() []cli.Command { 30 | return commands 31 | } 32 | -------------------------------------------------------------------------------- /common/mock_Commander.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import ( 6 | mock "github.com/stretchr/testify/mock" 7 | cli "github.com/urfave/cli" 8 | ) 9 | 10 | // MockCommander is an autogenerated mock type for the Commander type 11 | type MockCommander struct { 12 | mock.Mock 13 | } 14 | 15 | // Execute provides a mock function with given fields: c 16 | func (_m *MockCommander) Execute(c *cli.Context) { 17 | _m.Called(c) 18 | } 19 | -------------------------------------------------------------------------------- /common/mock_ExecutorData.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockExecutorData is an autogenerated mock type for the ExecutorData type 8 | type MockExecutorData struct { 9 | mock.Mock 10 | } 11 | -------------------------------------------------------------------------------- /common/mock_FailuresCollector.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockFailuresCollector is an autogenerated mock type for the FailuresCollector type 8 | type MockFailuresCollector struct { 9 | mock.Mock 10 | } 11 | 12 | // RecordFailure provides a mock function with given fields: reason, runnerDescription 13 | func (_m *MockFailuresCollector) RecordFailure(reason JobFailureReason, runnerDescription string) { 14 | _m.Called(reason, runnerDescription) 15 | } 16 | -------------------------------------------------------------------------------- /common/mock_SecretResolverRegistry.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockSecretResolverRegistry is an autogenerated mock type for the SecretResolverRegistry type 8 | type MockSecretResolverRegistry struct { 9 | mock.Mock 10 | } 11 | 12 | // GetFor provides a mock function with given fields: secret 13 | func (_m *MockSecretResolverRegistry) GetFor(secret Secret) (SecretResolver, error) { 14 | ret := _m.Called(secret) 15 | 16 | var r0 SecretResolver 17 | if rf, ok := ret.Get(0).(func(Secret) SecretResolver); ok { 18 | r0 = rf(secret) 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(SecretResolver) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func(Secret) error); ok { 27 | r1 = rf(secret) 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | 35 | // Register provides a mock function with given fields: f 36 | func (_m *MockSecretResolverRegistry) Register(f secretResolverFactory) { 37 | _m.Called(f) 38 | } 39 | -------------------------------------------------------------------------------- /common/mock_SecretsResolver.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockSecretsResolver is an autogenerated mock type for the SecretsResolver type 8 | type MockSecretsResolver struct { 9 | mock.Mock 10 | } 11 | 12 | // Resolve provides a mock function with given fields: secrets 13 | func (_m *MockSecretsResolver) Resolve(secrets Secrets) (JobVariables, error) { 14 | ret := _m.Called(secrets) 15 | 16 | var r0 JobVariables 17 | if rf, ok := ret.Get(0).(func(Secrets) JobVariables); ok { 18 | r0 = rf(secrets) 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(JobVariables) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func(Secrets) error); ok { 27 | r1 = rf(secrets) 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | -------------------------------------------------------------------------------- /common/mock_logger.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockLogger is an autogenerated mock type for the logger type 8 | type mockLogger struct { 9 | mock.Mock 10 | } 11 | 12 | // Println provides a mock function with given fields: args 13 | func (_m *mockLogger) Println(args ...interface{}) { 14 | var _ca []interface{} 15 | _ca = append(_ca, args...) 16 | _m.Called(_ca...) 17 | } 18 | 19 | // Warningln provides a mock function with given fields: args 20 | func (_m *mockLogger) Warningln(args ...interface{}) { 21 | var _ca []interface{} 22 | _ca = append(_ca, args...) 23 | _m.Called(_ca...) 24 | } 25 | -------------------------------------------------------------------------------- /common/mock_masker.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package common 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockMasker is an autogenerated mock type for the masker type 8 | type mockMasker struct { 9 | mock.Mock 10 | } 11 | 12 | // SetMasked provides a mock function with given fields: _a0 13 | func (_m *mockMasker) SetMasked(_a0 []string) { 14 | _m.Called(_a0) 15 | } 16 | -------------------------------------------------------------------------------- /common/test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | func Int64Ptr(v int64) *int64 { 4 | return &v 5 | } 6 | -------------------------------------------------------------------------------- /dockerfiles/ci/install_git_lfs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | downloadURL="https://github.com/git-lfs/git-lfs/releases/download/v${GIT_LFS_VERSION}/git-lfs-linux-amd64-v${GIT_LFS_VERSION}.tar.gz" 6 | 7 | wget "${downloadURL}" -O /tmp/git-lfs.tar.gz 8 | 9 | echo "${GIT_LFS_AMD64_CHECKSUM} /tmp/git-lfs.tar.gz" > /tmp/checksums 10 | sha256sum -c /tmp/checksums 11 | 12 | tar -xzf /tmp/git-lfs.tar.gz -C /tmp 13 | cp /tmp/git-lfs /usr/local/bin 14 | chmod +x /usr/local/bin/git-lfs 15 | 16 | git-lfs install --skip-repo 17 | 18 | rm -rf /tmp/* 19 | -------------------------------------------------------------------------------- /dockerfiles/fips/helper.fips.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION=1.17 2 | 3 | FROM go-fips:${GO_VERSION} 4 | 5 | WORKDIR /build 6 | COPY . /build/ 7 | 8 | ARG GOOS=linux 9 | ARG GOARCH=amd64 10 | 11 | RUN BASE_DIR="out/binaries/gitlab-runner-helper" && \ 12 | make "${BASE_DIR}/gitlab-runner-helper-fips" GOOS=${GOOS} GOARCH=${GOARCH} && \ 13 | ls "${BASE_DIR}"| grep gitlab-runner-helper| xargs -I '{}' mv "${BASE_DIR}/{}" /gitlab-runner-helper-fips 14 | -------------------------------------------------------------------------------- /dockerfiles/fips/runner.fips.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION=1.17 2 | 3 | FROM go-fips:${GO_VERSION} 4 | 5 | WORKDIR /build 6 | COPY . /build/ 7 | 8 | ARG GOOS=linux 9 | ARG GOARCH=amd64 10 | 11 | RUN make runner-bin-fips GOOS=${GOOS} GOARCH=${GOARCH} && \ 12 | cp out/binaries/* / 13 | -------------------------------------------------------------------------------- /dockerfiles/runner-helper/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | 3 | FROM $BASE_IMAGE 4 | 5 | # gitlab-runner-helper will try to resolve `sh` from the path. We ensure the PATH is populated by default, as some container runtimes do no longer set a default (e.g. containerd v1.2.8) 6 | ENV PATH="${PATH:-/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}" 7 | 8 | # hadolint ignore=DL3018 9 | RUN apk add --no-cache bash ca-certificates dumb-init git git-lfs miniperl \ 10 | && ln -s miniperl /usr/bin/perl 11 | 12 | RUN git lfs install --skip-repo 13 | 14 | COPY ./helpers/entrypoint / 15 | RUN chmod +x /entrypoint 16 | 17 | COPY ./scripts/ ./binaries/gitlab-runner-helper /usr/bin/ 18 | 19 | RUN echo 'hosts: files dns' >> /etc/nsswitch.conf 20 | 21 | # NOTE: The ENTRYPOINT metadata is not preserved on export, so we need to reapply this metadata on import. 22 | # See https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/2058#note_388341301 23 | ENTRYPOINT ["/usr/bin/dumb-init", "/entrypoint"] 24 | CMD ["sh"] 25 | -------------------------------------------------------------------------------- /dockerfiles/runner-helper/Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | 3 | FROM $BASE_IMAGE 4 | 5 | # gitlab-runner-helper will try to resolve `sh` from the path. We ensure the PATH is populated by default, as some container runtimes do no longer set a default (e.g. containerd v1.2.8) 6 | ENV PATH="${PATH:-/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}" 7 | 8 | RUN apt-get update && apt-get install -y bash ca-certificates dumb-init git git-lfs \ 9 | && rm -rf /var/lib/apt/lists/* 10 | 11 | RUN git lfs install --skip-repo 12 | 13 | COPY ./helpers/entrypoint / 14 | RUN chmod +x /entrypoint 15 | 16 | COPY ./scripts/ ./binaries/gitlab-runner-helper /usr/bin/ 17 | 18 | # NOTE: The ENTRYPOINT metadata is not preserved on export, so we need to reapply this metadata on import. 19 | # See https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/2058#note_388341301 20 | ENTRYPOINT ["/usr/bin/dumb-init", "/entrypoint"] 21 | CMD ["sh"] 22 | -------------------------------------------------------------------------------- /dockerfiles/runner-helper/binaries/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/dockerfiles/runner-helper/binaries/.gitkeep -------------------------------------------------------------------------------- /dockerfiles/runner-helper/helpers/checksum.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$TargetFile, 3 | [string]$ExpectedHash 4 | ) 5 | 6 | $hash = Get-FileHash -Path $TargetFile -Algorithm SHA256 7 | 8 | if (-not ($hash.Hash -eq $ExpectedHash)) { 9 | Write-Warning "SHA256 checksum for $TargetFile is invalid" 10 | exit 1 11 | } 12 | 13 | Write-Output "SHA256 checksum for $TargetFile is valid" 14 | exit 0 15 | -------------------------------------------------------------------------------- /dockerfiles/runner-helper/helpers/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # gitlab-runner data directory 4 | DATA_DIR="/etc/gitlab-runner" 5 | # custom certificate authority path 6 | CA_CERTIFICATES_PATH=${CA_CERTIFICATES_PATH:-$DATA_DIR/certs/ca.crt} 7 | LOCAL_CA_PATH="/usr/local/share/ca-certificates/ca.crt" 8 | 9 | update_ca() { 10 | echo "Updating CA certificates..." 11 | cp "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" 12 | update-ca-certificates --fresh >/dev/null 13 | } 14 | 15 | if [ -f "${CA_CERTIFICATES_PATH}" ]; then 16 | # update the ca if the custom ca is different than the current 17 | cmp -s "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" || update_ca 18 | fi 19 | 20 | # launch CMD passing all arguments 21 | exec "$@" 22 | -------------------------------------------------------------------------------- /dockerfiles/runner-helper/helpers/entrypoint.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | IF NOT DEFINED LOCAL_CA_PATH (SET LOCAL_CA_PATH="C:\GitLab-Runner\certs\ca.crt") 4 | 5 | IF EXIST %LOCAL_CA_PATH% ( 6 | echo "Adding CA certificate..." 7 | certutil -addstore "Root" %LOCAL_CA_PATH% 8 | ) 9 | 10 | %* 11 | -------------------------------------------------------------------------------- /dockerfiles/runner-helper/scripts/gitlab-runner-build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | umask 0000 3 | exec /bin/bash 4 | -------------------------------------------------------------------------------- /dockerfiles/runner/alpine/README.md: -------------------------------------------------------------------------------- 1 | `gitlab/gitlab-runner:alpine` is image with minimal footprint based on Alpine Linux that runs GitLab Runner. 2 | -------------------------------------------------------------------------------- /dockerfiles/runner/alpine/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # gitlab-runner data directory 4 | DATA_DIR="/etc/gitlab-runner" 5 | CONFIG_FILE=${CONFIG_FILE:-$DATA_DIR/config.toml} 6 | # custom certificate authority path 7 | CA_CERTIFICATES_PATH=${CA_CERTIFICATES_PATH:-$DATA_DIR/certs/ca.crt} 8 | LOCAL_CA_PATH="/usr/local/share/ca-certificates/ca.crt" 9 | 10 | update_ca() { 11 | echo "Updating CA certificates..." 12 | cp "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" 13 | update-ca-certificates --fresh >/dev/null 14 | } 15 | 16 | if [ -f "${CA_CERTIFICATES_PATH}" ]; then 17 | # update the ca if the custom ca is different than the current 18 | cmp -s "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" || update_ca 19 | fi 20 | 21 | # launch gitlab-runner passing all arguments 22 | exec gitlab-runner "$@" 23 | -------------------------------------------------------------------------------- /dockerfiles/runner/alpine/install-gitlab-runner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | ARCH="$1" 6 | 7 | mv "/usr/bin/gitlab-runner-linux-${ARCH}" /usr/bin/gitlab-runner 8 | 9 | chmod +x /usr/bin/gitlab-runner 10 | ln -s /usr/bin/gitlab-runner /usr/bin/gitlab-ci-multi-runner 11 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubi-fips/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | 3 | FROM $BASE_IMAGE 4 | 5 | ARG TARGETPLATFORM 6 | 7 | # hadolint ignore=DL3008 8 | RUN dnf update -y && \ 9 | dnf install -y \ 10 | openssl \ 11 | curl \ 12 | git \ 13 | wget \ 14 | openssh-clients \ 15 | && dnf clean all && \ 16 | rm -rf /var/cache/dnf 17 | 18 | ARG DOCKER_MACHINE_VERSION 19 | ARG DUMB_INIT_VERSION 20 | ARG GIT_LFS_VERSION 21 | 22 | COPY gitlab-runner_*.rpm checksums-* install-deps install-gitlab-runner /tmp/ 23 | RUN /tmp/install-deps "${TARGETPLATFORM}" "${DOCKER_MACHINE_VERSION}" "${DUMB_INIT_VERSION}" "${GIT_LFS_VERSION}" 24 | RUN rm -rf /tmp/* 25 | 26 | FROM $BASE_IMAGE 27 | 28 | COPY --from=0 / / 29 | COPY --chmod=777 entrypoint / 30 | 31 | ENV FIPS_ENABLED=1 32 | 33 | STOPSIGNAL SIGQUIT 34 | VOLUME ["/etc/gitlab-runner", "/home/gitlab-runner"] 35 | ENTRYPOINT ["/usr/bin/dumb-init", "/entrypoint"] 36 | CMD ["run", "--user=gitlab-runner", "--working-directory=/home/gitlab-runner"] 37 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubi-fips/README.md: -------------------------------------------------------------------------------- 1 | `gitlab/gitlab-runner:ubi-fips` is image that can be used to run GitLab Runner in container with FIPS mode enabled. 2 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubi-fips/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # gitlab-runner data directory 4 | DATA_DIR="/etc/gitlab-runner" 5 | CONFIG_FILE=${CONFIG_FILE:-$DATA_DIR/config.toml} 6 | # custom certificate authority path 7 | CA_CERTIFICATES_PATH=${CA_CERTIFICATES_PATH:-$DATA_DIR/certs/ca.crt} 8 | LOCAL_CA_PATH="/usr/local/share/ca-certificates/ca.crt" 9 | 10 | update_ca() { 11 | echo "Updating CA certificates..." 12 | cp "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" 13 | update-ca-certificates --fresh >/dev/null 14 | } 15 | 16 | if [ -f "${CA_CERTIFICATES_PATH}" ]; then 17 | # update the ca if the custom ca is different than the current 18 | cmp --silent "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" || update_ca 19 | fi 20 | 21 | # launch gitlab-runner passing all arguments 22 | exec gitlab-runner "$@" 23 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubi-fips/install-gitlab-runner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eEo pipefail 4 | 5 | ARCH=$(if [ "$1" == "ppc64le" ]; then echo "ppc64el"; else echo "$1"; fi) 6 | 7 | rpm -i "/tmp/gitlab-runner_${ARCH}-fips.rpm" 8 | dnf update -y 9 | dnf install -y gitlab-runner-fips 10 | dnf clean all 11 | rm -rf /var/cache/dnf 12 | rm "/tmp/gitlab-runner_${ARCH}-fips.rpm" 13 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubuntu/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | 3 | FROM $BASE_IMAGE 4 | 5 | ARG TARGETPLATFORM 6 | 7 | ENV DEBIAN_FRONTEND=noninteractive 8 | # hadolint ignore=DL3008 9 | RUN apt-get update -y && \ 10 | apt-get install -y --no-install-recommends \ 11 | apt-transport-https \ 12 | ca-certificates \ 13 | curl \ 14 | git \ 15 | wget \ 16 | tzdata \ 17 | openssh-client \ 18 | && rm -rf /var/lib/apt/lists/* 19 | 20 | ARG DOCKER_MACHINE_VERSION 21 | ARG DUMB_INIT_VERSION 22 | ARG GIT_LFS_VERSION 23 | 24 | COPY gitlab-runner_*.deb checksums-* install-deps install-gitlab-runner /tmp/ 25 | RUN /tmp/install-deps "${TARGETPLATFORM}" "${DOCKER_MACHINE_VERSION}" "${DUMB_INIT_VERSION}" "${GIT_LFS_VERSION}" 26 | RUN rm -rf /tmp/* 27 | 28 | FROM $BASE_IMAGE 29 | 30 | COPY --from=0 / / 31 | COPY --chmod=777 entrypoint / 32 | 33 | STOPSIGNAL SIGQUIT 34 | VOLUME ["/etc/gitlab-runner", "/home/gitlab-runner"] 35 | ENTRYPOINT ["/usr/bin/dumb-init", "/entrypoint"] 36 | CMD ["run", "--user=gitlab-runner", "--working-directory=/home/gitlab-runner"] 37 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubuntu/README.md: -------------------------------------------------------------------------------- 1 | `gitlab/gitlab-runner:latest` is image that can be used to run GitLab Runner in container. 2 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubuntu/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # gitlab-runner data directory 4 | DATA_DIR="/etc/gitlab-runner" 5 | CONFIG_FILE=${CONFIG_FILE:-$DATA_DIR/config.toml} 6 | # custom certificate authority path 7 | CA_CERTIFICATES_PATH=${CA_CERTIFICATES_PATH:-$DATA_DIR/certs/ca.crt} 8 | LOCAL_CA_PATH="/usr/local/share/ca-certificates/ca.crt" 9 | 10 | update_ca() { 11 | echo "Updating CA certificates..." 12 | cp "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" 13 | update-ca-certificates --fresh >/dev/null 14 | } 15 | 16 | if [ -f "${CA_CERTIFICATES_PATH}" ]; then 17 | # update the ca if the custom ca is different than the current 18 | cmp --silent "${CA_CERTIFICATES_PATH}" "${LOCAL_CA_PATH}" || update_ca 19 | fi 20 | 21 | # launch gitlab-runner passing all arguments 22 | exec gitlab-runner "$@" 23 | -------------------------------------------------------------------------------- /dockerfiles/runner/ubuntu/install-gitlab-runner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eEo pipefail 4 | 5 | ARCH=$(if [ "$1" == "ppc64le" ]; then echo "ppc64el"; else echo "$1"; fi) 6 | 7 | dpkg -i "/tmp/gitlab-runner_${ARCH}.deb" 8 | apt-get update 9 | apt-get -f install -y 10 | rm -rf /var/lib/apt/lists/* 11 | rm "/tmp/gitlab-runner_${ARCH}.deb" 12 | -------------------------------------------------------------------------------- /docs/.markdownlint/markdownlint-no-trailing-spaces.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Extended Markdown configuration to enforce no-trailing-spaces rule 3 | extends: "../../.markdownlint.yml" 4 | no-trailing-spaces: true 5 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Admin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.Admin 3 | # 4 | # Checks for "admin" and recommends using the full word instead. "Admin Area" is OK. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: substitution 8 | message: 'Verify this use of the word "admin". Can it be updated to "administration", "administrator", "administer", or "Admin Area"?' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html 10 | level: suggestion 11 | ignorecase: false 12 | swap: 13 | '[Aa]dmin ?\w*': '(?:Admin( Area| Mode)?|[Aa]dminist(ration|rator|rators|er|rative))' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/AlertBoxStyle.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.AlertBoxStyle 3 | # 4 | # Makes sure alert boxes are used with block quotes. Checks for 3 formatting issues: 5 | # 6 | # - Alert boxes inside a block quote (">") 7 | # - Alert boxes with the note text on the same line 8 | # - Alert boxes using words other than "NOTE" or "WARNING" 9 | # 10 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 11 | extends: existence 12 | message: 'Alert box "%s" must use the formatting in the style guide.' 13 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#alert-boxes 14 | level: error 15 | nonword: true 16 | scope: raw 17 | raw: 18 | - '(\n *\> *(?:NOTE|WARNING)|' 19 | - '\n\n(NOTE|WARNING):[^\n]|' 20 | - '\n\n *(?:> )?\**(Note|note|TIP|Tip|tip|CAUTION|Caution|caution|DANGER|Danger|danger|Warning|warning):.*)' 21 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/BadgeCapitalization.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.BadgeCapitalization 3 | # 4 | # Verifies that badges are not mixed case, which won't render properly. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Badge "%s" must be capitalized.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#product-tier-badges 10 | level: error 11 | scope: raw 12 | raw: 13 | - '(?!\*\*\((FREE|PREMIUM|ULTIMATE)( (SELF|SAAS))?\)\*\*)' 14 | - '(?i)\*\*\((free|premium|ultimate)( (self|saas))?\)\*\*' 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/CodeblockFences.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.CodeblockFences 3 | # 4 | # Ensures all codeblock language tags use the full name, not aliases. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Syntax highlighting hint "%s" must be one of: yaml, ruby, plaintext, markdown, javascript, shell, golang, python, dockerfile, or typescript.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#code-blocks 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\`\`\`(yml|rb|text|md|bash|sh\n|js\n|go\n|py\n|docker\n|ts)' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/CurlStringsQuoted.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.CurlStringsQuoted 3 | # 4 | # Ensures all code blocks using `curl` wrap URL strings in quotation marks. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'For consistency across all cURL examples, always wrap the URL in double quotes ("): %s' 9 | link: https://docs.gitlab.com/ee/development/documentation/restful_api_styleguide.html#curl-commands 10 | level: error 11 | scope: code 12 | raw: 13 | - 'curl [^"]+://.*' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/CurrentStatus.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.CurrentStatus 3 | # 4 | # Checks for words that indicate a product or feature may change in the future. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Avoid words like "%s" that promise future changes, because documentation is about the current state of the product.' 9 | level: suggestion 10 | ignorecase: true 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list 12 | tokens: 13 | - currently 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/DefaultBranch.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.DefaultBranch 3 | # 4 | # Do not refer to the default branch as the "master" branch, if possible. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Use "default branch" or `main` instead of `master`, when possible.' 9 | level: warning 10 | ignorecase: true 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html 12 | scope: raw 13 | raw: 14 | - '\`master\`' 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Dropdown.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.Dropdown 3 | # 4 | # Catches many ways the phrase 'dropdown list' can be fumbled. 5 | # 6 | # For a list of all options, see https://errata-ai.github.io/vale/styles/ 7 | extends: existence 8 | message: 'Use "dropdown list".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html#dropdown-list 10 | level: suggestion 11 | ignorecase: true 12 | tokens: 13 | - drop-down( [\w]*)? 14 | - dropdown(?! list) 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/ElementDescriptors.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.ElementDescriptors 3 | # 4 | # Suggests the correct way to describe elements in a form. 5 | # 6 | # For a list of all options, see https://errata-ai.github.io/vale/styles/ 7 | extends: substitution 8 | message: 'When describing elements, %s "%s".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language 10 | level: suggestion 11 | ignorecase: true 12 | swap: 13 | button: 'if possible, rewrite to remove' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/FirstPerson.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.FirstPerson 3 | # 4 | # Checks for use of first person pronouns. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: '"%s" is a first-person pronoun. Use second- or third-person pronouns (like we, you, us, one) instead.' 9 | level: warning 10 | ignorecase: true 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list 12 | tokens: 13 | - '\bI[ ,;:?!"]|\bI\x27.{1,2}' 14 | - me 15 | - myself 16 | - mine 17 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/FutureTense.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.FutureTense 3 | # 4 | # Checks for use of future tense in sentences. Present tense is strongly preferred. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Avoid using future tense: "%s". Use present tense instead.' 9 | ignorecase: true 10 | level: warning 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list 12 | raw: 13 | - "(going to( |\n|[[:punct:]])[a-zA-Z]*|" 14 | - "will( |\n|[[:punct:]])[a-zA-Z]*|" 15 | - "won't( |\n|[[:punct:]])[a-zA-Z]*|" 16 | - "[a-zA-Z]*'ll( |\n|[[:punct:]])[a-zA-Z]*)" 17 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/InclusionAbleism.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.InclusionAbleism 3 | # 4 | # Suggests alternatives for words that foster ableism. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: substitution 8 | message: 'Use inclusive language. Consider "%s" instead of "%s".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#inclusive-language 10 | level: suggestion 11 | ignorecase: true 12 | swap: 13 | sanity (?:check|test): check for completeness 14 | dummy: placeholder, sample, fake 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/InclusionCultural.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.InclusionCultural 3 | # 4 | # Suggests alternatives for words that are culturally inappropriate. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: substitution 8 | message: 'Use inclusive language. Consider "%s" instead of "%s".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#inclusive-language 10 | level: warning 11 | ignorecase: true 12 | swap: 13 | blacklist(?:ed|ing|s)?: denylist 14 | whitelist(?:ed|ing|s)?: allowlist 15 | master: primary, main 16 | slave: secondary 17 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/InclusionGender.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.InclusionGender 3 | # 4 | # Suggests alternatives for words that are gender-specific. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: substitution 8 | message: 'Use inclusive language. Consider "%s" instead of "%s".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#inclusive-language 10 | level: suggestion 11 | ignorecase: true 12 | swap: 13 | mankind: humanity, people 14 | manpower: GitLab team members 15 | he: they 16 | his: their 17 | she: they 18 | hers: their 19 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/InternalLinkCase.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.InternalLinkCase 3 | # 4 | # Checks that anchor fragments on internal links are in lower-case. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Links to subheadings in GitLab docs must be in lower-case: "%s"' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation 10 | level: error 11 | scope: raw 12 | raw: 13 | - '[^\`]\[[^\[\]]+\]\((https?:){0}[\w\/\.]*?#[^\s]*?[A-Z][^\) ]*\)[^\`]' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/InternalLinkExtension.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.InternalLinkExtension 3 | # 4 | # Checks that internal links have .md extenstion and not .html extension. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Link "%s" must use the .md file extension.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\[.+\]\([\w\/\.-]+\.html[^)]*\)' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/InternalLinkFormat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.InternalLinkFormat 3 | # 4 | # Checks that internal link paths don't start with "./", which is not needed. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Link "%s" must not start with "./".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\[.+\]\(\.\/.*?\)' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/LatinTerms.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.LatinTerms 3 | # 4 | # Checks for use of Latin terms. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: substitution 8 | message: 'Use "%s" instead of "%s", but consider rewriting the sentence.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list 10 | level: warning 11 | nonword: true 12 | ignorecase: true 13 | swap: 14 | e\.g\.: for example 15 | e\. g\.: for example 16 | i\.e\.: that is 17 | i\. e\.: that is 18 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/MeaningfulLinkWords.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.MeaningfulLinkWords 3 | # 4 | # Checks for the presence of semantically unhelpful words in link text. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Improve SEO and accessibility by rewriting "%s" in the link text.' 9 | level: warning 10 | scope: link 11 | ignorecase: true 12 | link: https://about.gitlab.com/handbook/communication/#writing-style-guidelines 13 | tokens: 14 | - here 15 | - this page 16 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/MergeConflictMarkers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.MergeConflictMarkers 3 | # 4 | # Checks for the presence of merge conflict markers. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Merge conflict marker "%s" found.' 9 | link: https://docs.gitlab.com/ee/development/code_review.html#merging-a-merge-request 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\n<<<<<<< .+\n|\n=======\n|\n>>>>>>> .+\n' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/NonStandardQuotes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.NonStandardQuotes 3 | # 4 | # Use only standard single and double quotes, not left or right quotes. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Use standard single quotes or double quotes only. Do not use left or right quotes.' 9 | level: warning 10 | ignorecase: true 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html 12 | scope: raw 13 | raw: 14 | - '[‘’“”]' 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/OutdatedVersions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.OutdatedVersions 3 | # 4 | # Checks for references to versions of GitLab that are no longer supported. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Can this reference to "%s" be refactored?' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#gitlab-versions 10 | level: suggestion 11 | nonword: true 12 | ignorecase: true 13 | tokens: 14 | - "GitLab (v)?2." 15 | - "GitLab (v)?3." 16 | - "GitLab (v)?4." 17 | - "GitLab (v)?5." 18 | - "GitLab (v)?6." 19 | - "GitLab (v)?7." 20 | - "GitLab (v)?8." 21 | - "GitLab (v)?9." 22 | - "GitLab (v)?10." 23 | - "GitLab (v)?11." 24 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/OxfordComma.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.OxfordComma 3 | # 4 | # Checks for the lack of an Oxford comma. In some cases, will catch overly complex sentence structures with lots of commas. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Use a comma before the last "and" or "or" in a list of four or more items.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#punctuation 10 | level: warning 11 | raw: 12 | - '(?:[\w-_` ]+,){2,}(?:[\w-_` ]+) (and |or )' 13 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Possessive.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.Possessive 3 | # 4 | # The word GitLab should not be used in the possessive form. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: "Rewrite '%s' to not use 's." 9 | level: error 10 | ignorecase: true 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#trademark 12 | tokens: 13 | - GitLab's 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/ReadingLevel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.ReadingLevel 3 | # 4 | # Checks the Flesch-Kincaid reading level. 5 | # 6 | # https://docs.errata.ai/vale/styles#metric 7 | extends: metric 8 | message: "The grade level - %s - refers to how hard the content is to understand. Aim for 8th grade or lower by using shorter sentences and words." 9 | link: https://docs.gitlab.com/ee/development/documentation/testing.html#vale-readability-score 10 | level: suggestion 11 | formula: | 12 | (0.39 * (words / sentences)) + (11.8 * (syllables / words)) - 15.59 13 | condition: "> 1" 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/ReferenceLinks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.ReferenceLinks 3 | # 4 | # Checks for reference-style links that should be converted to inline links. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Link "%s" must be inline.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#basic-link-criteria 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\n\[[^\]]*\]: .*' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/RelativeLinks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.RelativeLinks 3 | # 4 | # Checks for the presence of absolute hyperlinks that should be relative. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Link "%s" must be a relative link with a .md extension.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\[.+\]\(https?:\/\/docs\.gitlab\.com\/runner.*\)' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/RelativeLinksDoubleSlashes.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.RelativeLinksDoubleSlashes 3 | # 4 | # Checks for the presence of double slashes in relative URLs. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Relative links must not include a double slash.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation 10 | level: error 11 | scope: raw 12 | raw: 13 | - '\.//' 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Repetition.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.Repetition 3 | # 4 | # Checks for duplicate words, like `the the` or `and and`. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: repetition 8 | message: '"%s" is repeated.' 9 | level: error 10 | alpha: true 11 | tokens: 12 | - '[^\s]+' 13 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/SentenceLength.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.SentenceLength 3 | # 4 | # Counts words in a sentence and alerts if a sentence exceeds 25 words. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: occurrence 8 | message: 'Shorter sentences improve readability (max 25 words).' 9 | scope: sentence 10 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language 11 | level: warning 12 | max: 25 13 | token: \b(\w+)\b 14 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/SentenceSpacing.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.SentenceSpacing 3 | # 4 | # Checks for incorrect spacing (no spaces, or more than one space) around punctuation. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: '"%s" must contain one and only one space.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#punctuation 10 | level: error 11 | nonword: true 12 | tokens: 13 | - '[a-z][.?!,][A-Z]' 14 | - '[\w.?!,\(\)\-":] {2,}[\w.?!,\(\)\-":]' 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Simplicity.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.Simplicity 3 | # 4 | # Checks for words implying ease of use, to avoid cognitive dissonance for frustrated users. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: existence 8 | message: 'Avoid words like "%s" that imply ease of use, because the user may find this action hard.' 9 | level: suggestion 10 | ignorecase: true 11 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#usage-list 12 | tokens: 13 | - easy 14 | - easily 15 | - handy 16 | - simple 17 | - simply 18 | - useful 19 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Spelling.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.Spelling 3 | # 4 | # Checks for possible spelling mistakes in content, not code. Results from links using angle brackets () should be corrected. 5 | # 6 | # If a word is flagged as a spelling mistake incorrectly, such as a product name, 7 | # you can submit an MR to update `spelling-exceptions.txt` with the missing word. 8 | # Commands, like `git clone` must use backticks, and must not be added to the 9 | # exceptions. 10 | # 11 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 12 | extends: spelling 13 | message: 'Spelling check: "%s"?' 14 | level: warning 15 | ignore: 16 | - gitlab/spelling-exceptions.txt 17 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/SubstitutionSuggestions.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.SubstitutionSuggestions 3 | # 4 | # Suggests better options for frequently misused terms that are often - but not always - incorrect. 5 | # SubstitutionWarning.yml and Substitutions.yml also exist. 6 | # 7 | # For a list of all options, see https://errata-ai.github.io/vale/styles/ 8 | extends: substitution 9 | message: 'Consider %s instead of "%s".' 10 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language 11 | level: suggestion 12 | ignorecase: true 13 | swap: 14 | active user: '"billable user"' 15 | active users: '"billable users"' 16 | docs: '"documentation"' 17 | e-mail: '"email"' 18 | GFM: '"GitLab Flavored Markdown"' 19 | it is recommended: '"we recommend"' 20 | OAuth2: '"OAuth 2.0"' 21 | once that: '"after that"' 22 | once the: '"after the"' 23 | once you: '"after you"' 24 | since: '"because" or "after"' 25 | sub-group: '"subgroup"' 26 | sub-groups: '"subgroups"' 27 | within: '"in"' 28 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/SubstitutionWarning.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.SubstitutionWarning 3 | # 4 | # Checks for misused terms or common shorthand that should never be used at GitLab, but can't be flagged as errors. 5 | # Substitutions.yml and SubstitionSuggestions.yml also exist. 6 | # 7 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 8 | extends: substitution 9 | message: 'If possible, use "%s" instead of "%s".' 10 | link: https://about.gitlab.com/handbook/communication/#top-misused-terms 11 | level: warning 12 | ignorecase: true 13 | swap: 14 | click: select 15 | code base: codebase 16 | config: configuration 17 | distro: distribution 18 | file name: filename 19 | filesystem: file system 20 | info: information 21 | need to: must 22 | repo: repository 23 | timezone: time zone 24 | utilize: use 25 | administrator permission: the administrator access level 26 | administrator permissions: the administrator access level 27 | administrator role: the administrator access level 28 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/ToDo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.ToDo 3 | # 4 | # You should not use "To Do", unless it refers to the UI element. 5 | # 6 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 7 | extends: substitution 8 | message: 'Use "to-do item" in most cases, or "Add a to do" if referring to the UI button.' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#feature-names 10 | level: warning 11 | ignorecase: false 12 | swap: 13 | '[Tt]o [Dd]o [Ii]tems?': to-do item 14 | '\w* [Aa] [Tt]o [Dd]o': Add a to do 15 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/UnclearAntecedent.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Warning: gitlab.UnclearAntecedent 3 | # 4 | # Checks for words that need a noun for clarity. 5 | # 6 | # For a list of all options, see https://docs.errata.ai/vale/styles 7 | extends: existence 8 | message: "'%s' is not precise. Try rewriting with a specific subject and verb." 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html#this-these-that-those 10 | level: warning 11 | ignorecase: false 12 | tokens: 13 | - 'That is' 14 | - 'That was' 15 | - 'These are' 16 | - 'These were' 17 | - 'There are' 18 | - 'There were' 19 | - 'This is' 20 | - 'This was' 21 | - 'Those are' 22 | - 'Those were' 23 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/VersionText.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Error: gitlab.VersionText 3 | # 4 | # Checks that version text is formatted correctly. 5 | # 6 | # Specifically looks for either of the following that is immediately followed on the next line 7 | # by content, which will break rendering: 8 | # 9 | # - `> Introduced` (version text without a link) 10 | # - `> [Introduced` (version text with a link) 11 | # 12 | # Because it excludes the prefix `> - `, it doesn't look for multi-line version text, for which 13 | # content immediately on the next line is ok. However, this will often highlight where multi-line 14 | # version text is attempted without `-` characters. 15 | # 16 | # For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles 17 | extends: existence 18 | message: 'This introduced-in line is not formatted correctly.' 19 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#version-text-in-the-version-history 20 | level: error 21 | scope: raw 22 | raw: 23 | - '> \[?Introduced.+\n[^\n]' 24 | -------------------------------------------------------------------------------- /docs/.vale/gitlab/Wordy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Suggestion: gitlab.Wordy 3 | # 4 | # Suggests shorter versions of wordy phrases. 5 | # 6 | # For a list of all options, see https://docs.errata.ai/vale/styles 7 | extends: substitution 8 | message: 'Be concise: "%s" is less wordy than "%s".' 9 | link: https://docs.gitlab.com/ee/development/documentation/styleguide/word_list.html 10 | level: suggestion 11 | ignorecase: true 12 | swap: 13 | in order to: to 14 | -------------------------------------------------------------------------------- /docs/configuration/configuring_runner_openshift.md: -------------------------------------------------------------------------------- 1 | --- 2 | redirect_to: 'configuring_runner_operator.md' 3 | remove_date: '2022-06-30' 4 | --- 5 | 6 | This document was moved to [another location](configuring_runner_operator). 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /docs/configuration/img/autoscale-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/docs/configuration/img/autoscale-example.png -------------------------------------------------------------------------------- /docs/configuration/img/autoscale-state-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/docs/configuration/img/autoscale-state-chart.png -------------------------------------------------------------------------------- /docs/configuration/img/runner_fargate_driver_ssh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/docs/configuration/img/runner_fargate_driver_ssh.png -------------------------------------------------------------------------------- /docs/executors/parallels.md: -------------------------------------------------------------------------------- 1 | # Parallels 2 | 3 | Check the [VirtualBox executor](virtualbox.md) to see how to configure the 4 | **parallels** executor. 5 | -------------------------------------------------------------------------------- /docs/install/img/openshift_allitems_v13_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/docs/install/img/openshift_allitems_v13_3.png -------------------------------------------------------------------------------- /docs/install/img/openshift_installoperator_v13_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/docs/install/img/openshift_installoperator_v13_3.png -------------------------------------------------------------------------------- /docs/install/img/openshift_success_v13_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veertuinc/gitlab-runner/05ff95dcc532df1003cc246e96ef4862b1fd9217/docs/install/img/openshift_success_v13_3.png -------------------------------------------------------------------------------- /docs/install/openshift.md: -------------------------------------------------------------------------------- 1 | --- 2 | redirect_to: 'operator.md' 3 | remove_date: '2022-06-29' 4 | --- 5 | 6 | This document was moved to [another location](operator.md). 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /executors/custom/api/config.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | // ConfigExecOutput defines the output structure of the config_exec call. 4 | // 5 | // This should be used to pass the configuration values from Custom Executor 6 | // driver to the Runner. 7 | type ConfigExecOutput struct { 8 | Driver *DriverInfo `json:"driver,omitempty"` 9 | 10 | Hostname *string `json:"hostname,omitempty"` 11 | BuildsDir *string `json:"builds_dir,omitempty"` 12 | CacheDir *string `json:"cache_dir,omitempty"` 13 | 14 | BuildsDirIsShared *bool `json:"builds_dir_is_shared,omitempty"` 15 | 16 | JobEnv *map[string]string `json:"job_env,omitempty"` 17 | } 18 | 19 | // DriverInfo wraps the information about Custom Executor driver details 20 | // like the name or version 21 | type DriverInfo struct { 22 | Name *string `json:"name,omitempty"` 23 | Version *string `json:"version,omitempty"` 24 | } 25 | -------------------------------------------------------------------------------- /executors/custom/api/const.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | const ( 4 | // The name of the variable used to pass the value of Build failure exit code 5 | // that should be returned from Custom executor driver 6 | BuildFailureExitCodeVariable = "BUILD_FAILURE_EXIT_CODE" 7 | 8 | // The name of the variable used to pass the value of System failure exit code 9 | // that should be returned from Custom executor driver 10 | SystemFailureExitCodeVariable = "SYSTEM_FAILURE_EXIT_CODE" 11 | 12 | // The name of the variable used to pass the value of path to the file that 13 | // contains JSON encoded content of job API received from GitLab's API 14 | JobResponseFileVariable = "JOB_RESPONSE_FILE" 15 | ) 16 | -------------------------------------------------------------------------------- /executors/custom/command/errors.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ErrUnknownFailure struct { 8 | Inner error 9 | ExitCode int 10 | } 11 | 12 | func (e *ErrUnknownFailure) Error() string { 13 | return fmt.Sprintf( 14 | "unknown Custom executor executable exit code %d; executable execution terminated with: %v", 15 | e.ExitCode, 16 | e.Inner, 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /executors/custom/command/mock_Command.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package command 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockCommand is an autogenerated mock type for the Command type 8 | type MockCommand struct { 9 | mock.Mock 10 | } 11 | 12 | // Run provides a mock function with given fields: 13 | func (_m *MockCommand) Run() error { 14 | ret := _m.Called() 15 | 16 | var r0 error 17 | if rf, ok := ret.Get(0).(func() error); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Error(0) 21 | } 22 | 23 | return r0 24 | } 25 | -------------------------------------------------------------------------------- /executors/custom/consts.go: -------------------------------------------------------------------------------- 1 | package custom 2 | 3 | import "time" 4 | 5 | const defaultConfigExecTimeout = time.Hour 6 | const defaultPrepareExecTimeout = time.Hour 7 | const defaultCleanupExecTimeout = time.Hour 8 | -------------------------------------------------------------------------------- /executors/custom/terminal.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package custom 5 | 6 | import ( 7 | "errors" 8 | 9 | terminalsession "gitlab.com/gitlab-org/gitlab-runner/session/terminal" 10 | ) 11 | 12 | func (e *executor) Connect() (terminalsession.Conn, error) { 13 | return nil, errors.New("not yet supported") 14 | } 15 | -------------------------------------------------------------------------------- /executors/custom/terminal_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration && !windows 2 | // +build !integration,!windows 3 | 4 | package custom 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestExecutor_Connect(t *testing.T) { 13 | e := new(executor) 14 | connection, err := e.Connect() 15 | 16 | assert.Nil(t, connection) 17 | assert.EqualError(t, err, "not yet supported") 18 | } 19 | -------------------------------------------------------------------------------- /executors/custom/testdata/test_executor/.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | main.exe 3 | 4 | -------------------------------------------------------------------------------- /executors/docker/config_updater.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "strings" 5 | 6 | "gitlab.com/gitlab-org/gitlab-runner/common" 7 | ) 8 | 9 | func configUpdater(input *common.RunnerConfig, output *common.ConfigInfo) { 10 | if input.RunnerSettings.Docker != nil { 11 | output.Gpus = strings.Trim(input.RunnerSettings.Docker.Gpus, " ") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /executors/docker/config_updater_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package docker 5 | 6 | import ( 7 | "strings" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "gitlab.com/gitlab-org/gitlab-runner/common" 12 | ) 13 | 14 | func TestDockerConfigUpdate(t *testing.T) { 15 | testCases := map[string]struct { 16 | gpus string 17 | }{ 18 | "gpus set to all": { 19 | gpus: "all", 20 | }, 21 | "gpus with trailing space": { 22 | gpus: " ", 23 | }, 24 | } 25 | 26 | for tn, tc := range testCases { 27 | t.Run(tn, func(t *testing.T) { 28 | config := common.RunnerConfig{ 29 | RunnerSettings: common.RunnerSettings{Docker: &common.DockerConfig{Gpus: tc.gpus}}, 30 | } 31 | 32 | info := common.ConfigInfo{} 33 | configUpdater(&config, &info) 34 | assert.Equal(t, strings.Trim(tc.gpus, " "), info.Gpus) 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /executors/docker/consts.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import "time" 4 | 5 | const prebuiltImageExtension = ".tar.xz" 6 | 7 | const dockerCleanupTimeout = 5 * time.Minute 8 | 9 | const waitForContainerTimeout = 15 * time.Second 10 | 11 | const osTypeLinux = "linux" 12 | const osTypeWindows = "windows" 13 | 14 | const metadataOSType = "OSType" 15 | -------------------------------------------------------------------------------- /executors/docker/internal/exec/mock_Docker.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package exec 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockDocker is an autogenerated mock type for the Docker type 12 | type MockDocker struct { 13 | mock.Mock 14 | } 15 | 16 | // Exec provides a mock function with given fields: ctx, containerID, streams 17 | func (_m *MockDocker) Exec(ctx context.Context, containerID string, streams IOStreams) error { 18 | ret := _m.Called(ctx, containerID, streams) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(context.Context, string, IOStreams) error); ok { 22 | r0 = rf(ctx, containerID, streams) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | -------------------------------------------------------------------------------- /executors/docker/internal/exec/mock_reader.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package exec 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockReader is an autogenerated mock type for the reader type 8 | type mockReader struct { 9 | mock.Mock 10 | } 11 | 12 | // Read provides a mock function with given fields: p 13 | func (_m *mockReader) Read(p []byte) (int, error) { 14 | ret := _m.Called(p) 15 | 16 | var r0 int 17 | if rf, ok := ret.Get(0).(func([]byte) int); ok { 18 | r0 = rf(p) 19 | } else { 20 | r0 = ret.Get(0).(int) 21 | } 22 | 23 | var r1 error 24 | if rf, ok := ret.Get(1).(func([]byte) error); ok { 25 | r1 = rf(p) 26 | } else { 27 | r1 = ret.Error(1) 28 | } 29 | 30 | return r0, r1 31 | } 32 | -------------------------------------------------------------------------------- /executors/docker/internal/labels/mock_Labeler.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package labels 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockLabeler is an autogenerated mock type for the Labeler type 8 | type MockLabeler struct { 9 | mock.Mock 10 | } 11 | 12 | // Labels provides a mock function with given fields: otherLabels 13 | func (_m *MockLabeler) Labels(otherLabels map[string]string) map[string]string { 14 | ret := _m.Called(otherLabels) 15 | 16 | var r0 map[string]string 17 | if rf, ok := ret.Get(0).(func(map[string]string) map[string]string); ok { 18 | r0 = rf(otherLabels) 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(map[string]string) 22 | } 23 | } 24 | 25 | return r0 26 | } 27 | -------------------------------------------------------------------------------- /executors/docker/internal/networks/mock_debugLogger.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package networks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockDebugLogger is an autogenerated mock type for the debugLogger type 8 | type mockDebugLogger struct { 9 | mock.Mock 10 | } 11 | 12 | // Debugln provides a mock function with given fields: args 13 | func (_m *mockDebugLogger) Debugln(args ...interface{}) { 14 | var _ca []interface{} 15 | _ca = append(_ca, args...) 16 | _m.Called(_ca...) 17 | } 18 | -------------------------------------------------------------------------------- /executors/docker/internal/networks/utils.go: -------------------------------------------------------------------------------- 1 | package networks 2 | 3 | type debugLogger interface { 4 | Debugln(args ...interface{}) 5 | } 6 | -------------------------------------------------------------------------------- /executors/docker/internal/pull/mock_Manager.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package pull 4 | 5 | import ( 6 | types "github.com/docker/docker/api/types" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // MockManager is an autogenerated mock type for the Manager type 11 | type MockManager struct { 12 | mock.Mock 13 | } 14 | 15 | // GetDockerImage provides a mock function with given fields: imageName 16 | func (_m *MockManager) GetDockerImage(imageName string) (*types.ImageInspect, error) { 17 | ret := _m.Called(imageName) 18 | 19 | var r0 *types.ImageInspect 20 | if rf, ok := ret.Get(0).(func(string) *types.ImageInspect); ok { 21 | r0 = rf(imageName) 22 | } else { 23 | if ret.Get(0) != nil { 24 | r0 = ret.Get(0).(*types.ImageInspect) 25 | } 26 | } 27 | 28 | var r1 error 29 | if rf, ok := ret.Get(1).(func(string) error); ok { 30 | r1 = rf(imageName) 31 | } else { 32 | r1 = ret.Error(1) 33 | } 34 | 35 | return r0, r1 36 | } 37 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/manager_integration_unix_test.go: -------------------------------------------------------------------------------- 1 | //nolint:lll 2 | //go:build integration && (aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris) 3 | // +build integration 4 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 5 | 6 | package volumes_test 7 | 8 | import ( 9 | "testing" 10 | 11 | "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/volumes/parser" 12 | ) 13 | 14 | var testCreateVolumesLabelsDestinationPath = "/test" 15 | 16 | func TestCreateVolumesLabels(t *testing.T) { 17 | testCreateVolumesLabels(t, parser.NewLinuxParser()) 18 | } 19 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/manager_integration_windows_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration 2 | // +build integration 3 | 4 | package volumes_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/volumes/parser" 10 | ) 11 | 12 | var testCreateVolumesLabelsDestinationPath = `C:\test` 13 | 14 | func TestCreateVolumesLabels(t *testing.T) { 15 | testCreateVolumesLabels(t, parser.NewWindowsParser()) 16 | } 17 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/mock_debugLogger.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package volumes 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockDebugLogger is an autogenerated mock type for the debugLogger type 8 | type mockDebugLogger struct { 9 | mock.Mock 10 | } 11 | 12 | // Debugln provides a mock function with given fields: args 13 | func (_m *mockDebugLogger) Debugln(args ...interface{}) { 14 | var _ca []interface{} 15 | _ca = append(_ca, args...) 16 | _m.Called(_ca...) 17 | } 18 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/parser/errors.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type InvalidVolumeSpecError struct { 8 | spec string 9 | } 10 | 11 | func (e *InvalidVolumeSpecError) Error() string { 12 | return fmt.Sprintf("invalid volume specification: %q", e.spec) 13 | } 14 | 15 | func NewInvalidVolumeSpecErr(spec string) error { 16 | return &InvalidVolumeSpecError{ 17 | spec: spec, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | type Parser interface { 4 | ParseVolume(spec string) (*Volume, error) 5 | Path() Path 6 | } 7 | 8 | type Path interface { 9 | Join(elem ...string) string 10 | IsAbs(path string) bool 11 | IsRoot(path string) bool 12 | Contains(basePath, targetPath string) bool 13 | } 14 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/permission/mock_Setter.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package permission 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockSetter is an autogenerated mock type for the Setter type 12 | type MockSetter struct { 13 | mock.Mock 14 | } 15 | 16 | // Set provides a mock function with given fields: ctx, volumeName, labels 17 | func (_m *MockSetter) Set(ctx context.Context, volumeName string, labels map[string]string) error { 18 | ret := _m.Called(ctx, volumeName, labels) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(context.Context, string, map[string]string) error); ok { 22 | r0 = rf(ctx, volumeName, labels) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/permission/set.go: -------------------------------------------------------------------------------- 1 | package permission 2 | 3 | import "context" 4 | 5 | type Setter interface { 6 | Set(ctx context.Context, volumeName string, labels map[string]string) error 7 | } 8 | -------------------------------------------------------------------------------- /executors/docker/internal/volumes/permission/windows_set.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package permission 5 | 6 | import ( 7 | "context" 8 | ) 9 | 10 | type dockerWindowsSetter struct { 11 | } 12 | 13 | // NewDockerWindowsSetter is a noop permissions for Windows, this will be 14 | // implemented in https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25480. 15 | func NewDockerWindowsSetter() Setter { 16 | return &dockerWindowsSetter{} 17 | } 18 | 19 | // Set noop 20 | func (d dockerWindowsSetter) Set(_ context.Context, _ string, _ map[string]string) error { 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /executors/docker/internal/wait/mock_Waiter.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package wait 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockWaiter is an autogenerated mock type for the Waiter type 12 | type MockWaiter struct { 13 | mock.Mock 14 | } 15 | 16 | // Wait provides a mock function with given fields: ctx, containerID 17 | func (_m *MockWaiter) Wait(ctx context.Context, containerID string) error { 18 | ret := _m.Called(ctx, containerID) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { 22 | r0 = rf(ctx, containerID) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | -------------------------------------------------------------------------------- /executors/docker/labeler.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/labels" 4 | 5 | func (e *executor) createLabeler() error { 6 | e.labeler = labels.NewLabeler(e.Build) 7 | return nil 8 | } 9 | -------------------------------------------------------------------------------- /executors/docker/machine/collector_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package machine 5 | 6 | import ( 7 | "testing" 8 | "time" 9 | 10 | "gitlab.com/gitlab-org/gitlab-runner/common" 11 | 12 | "github.com/prometheus/client_golang/prometheus" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestIfMachineProviderExposesCollectInterface(t *testing.T) { 17 | var provider common.ExecutorProvider = &machineProvider{} 18 | collector, ok := provider.(prometheus.Collector) 19 | assert.True(t, ok) 20 | assert.NotNil(t, collector) 21 | } 22 | 23 | func TestMachineProviderDeadInterval(t *testing.T) { 24 | provider := newMachineProvider("docker_machines", "docker") 25 | assert.Equal(t, 0, provider.collectDetails().Idle) 26 | 27 | details := provider.machineDetails("test", false) 28 | assert.Equal(t, 1, provider.collectDetails().Idle) 29 | 30 | details.LastSeen = time.Now().Add(-machineDeadInterval) 31 | assert.Equal(t, 0, provider.collectDetails().Idle) 32 | } 33 | -------------------------------------------------------------------------------- /executors/docker/machine/consts.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "time" 4 | 5 | var provisionRetryInterval = time.Second 6 | var machineDeadInterval = 20 * time.Minute 7 | var removeRetryInterval = 30 * time.Second 8 | var removeRetryTries = 3 9 | var machineStopCommandTimeout = 1 * time.Minute 10 | -------------------------------------------------------------------------------- /executors/docker/machine/state.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | type machineState int 4 | 5 | const ( 6 | machineStateIdle machineState = iota 7 | machineStateAcquired 8 | machineStateCreating 9 | machineStateUsed 10 | machineStateRemoving 11 | ) 12 | 13 | func (t machineState) String() string { 14 | switch t { 15 | case machineStateIdle: 16 | return "Idle" 17 | case machineStateAcquired: 18 | return "Acquired" 19 | case machineStateCreating: 20 | return "Creating" 21 | case machineStateUsed: 22 | return "Used" 23 | case machineStateRemoving: 24 | return "Removing" 25 | default: 26 | return "Unknown" 27 | } 28 | } 29 | 30 | func (t machineState) MarshalText() ([]byte, error) { 31 | return []byte(t.String()), nil 32 | } 33 | -------------------------------------------------------------------------------- /executors/docker/machine/terminal.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "errors" 5 | 6 | "gitlab.com/gitlab-org/gitlab-runner/session/terminal" 7 | ) 8 | 9 | func (e *machineExecutor) Connect() (terminal.Conn, error) { 10 | if term, ok := e.executor.(terminal.InteractiveTerminal); ok { 11 | return term.Connect() 12 | } 13 | 14 | return nil, errors.New("executor does not have terminal") 15 | } 16 | -------------------------------------------------------------------------------- /executors/docker/machine/terminal_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package machine 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | "gitlab.com/gitlab-org/gitlab-runner/common" 11 | "gitlab.com/gitlab-org/gitlab-runner/session/terminal" 12 | ) 13 | 14 | func TestMachineExecutor_Connect_NoTerminal(t *testing.T) { 15 | e := machineExecutor{ 16 | executor: &common.MockExecutor{}, 17 | } 18 | 19 | conn, err := e.Connect() 20 | assert.Error(t, err) 21 | assert.Nil(t, conn) 22 | } 23 | 24 | type mockTerminalExecutor struct { 25 | common.MockExecutor 26 | terminal.MockInteractiveTerminal 27 | } 28 | 29 | func TestMachineExecutor_Connect_Terminal(t *testing.T) { 30 | mock := mockTerminalExecutor{} 31 | e := machineExecutor{ 32 | executor: &mock, 33 | } 34 | mock.MockInteractiveTerminal.On("Connect").Return(&terminal.MockConn{}, nil).Once() 35 | 36 | conn, err := e.Connect() 37 | assert.NoError(t, err) 38 | assert.NotNil(t, conn) 39 | mock.MockInteractiveTerminal.AssertCalled(t, "Connect") 40 | } 41 | -------------------------------------------------------------------------------- /executors/docker/network.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/networks" 5 | ) 6 | 7 | var createNetworksManager = func(e *executor) (networks.Manager, error) { 8 | networksManager := networks.NewManager(&e.BuildLogger, e.client, e.Build, e.labeler) 9 | 10 | return networksManager, nil 11 | } 12 | 13 | func (e *executor) createNetworksManager() error { 14 | nm, err := createNetworksManager(e) 15 | if err != nil { 16 | return err 17 | } 18 | e.networksManager = nm 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /executors/docker/pull.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/pull" 5 | ) 6 | 7 | var createPullManager = func(e *executor) (pull.Manager, error) { 8 | config := pull.ManagerConfig{ 9 | DockerConfig: e.Config.Docker, 10 | AuthConfig: e.Build.GetDockerAuthConfig(), 11 | ShellUser: e.Shell().User, 12 | Credentials: e.Build.Credentials, 13 | } 14 | 15 | pullManager := pull.NewManager(e.Context, &e.BuildLogger, config, e.client, func() { 16 | e.SetCurrentStage(ExecutorStagePullingImage) 17 | }) 18 | 19 | return pullManager, nil 20 | } 21 | 22 | func (e *executor) createPullManager() error { 23 | pm, err := createPullManager(e) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | e.pullManager = pm 29 | 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /executors/docker/tty.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import "github.com/docker/docker/api/types" 4 | 5 | func newDockerTTY(hijackedResp *types.HijackedResponse) *dockerTTY { 6 | return &dockerTTY{ 7 | hijackedResp: hijackedResp, 8 | } 9 | } 10 | 11 | type dockerTTY struct { 12 | hijackedResp *types.HijackedResponse 13 | } 14 | 15 | func (d *dockerTTY) Read(p []byte) (int, error) { 16 | return d.hijackedResp.Reader.Read(p) 17 | } 18 | 19 | func (d *dockerTTY) Write(p []byte) (int, error) { 20 | return d.hijackedResp.Conn.Write(p) 21 | } 22 | 23 | func (d *dockerTTY) Close() error { 24 | d.hijackedResp.Close() 25 | _ = d.hijackedResp.CloseWrite() 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /executors/docker/volume.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "gitlab.com/gitlab-org/gitlab-runner/executors/docker/internal/volumes" 5 | ) 6 | 7 | var createVolumesManager = func(e *executor) (volumes.Manager, error) { 8 | config := volumes.ManagerConfig{ 9 | CacheDir: e.Config.Docker.CacheDir, 10 | BasePath: e.Build.FullProjectDir(), 11 | UniqueName: e.Build.ProjectUniqueName(), 12 | TemporaryName: e.getProjectUniqRandomizedName(), 13 | DisableCache: e.Config.Docker.DisableCache, 14 | } 15 | 16 | if e.newVolumePermissionSetter != nil { 17 | setter, err := e.newVolumePermissionSetter() 18 | if err != nil { 19 | return nil, err 20 | } 21 | config.PermissionSetter = setter 22 | } 23 | 24 | volumesManager := volumes.NewManager(&e.BuildLogger, e.volumeParser, e.client, config, e.labeler) 25 | 26 | return volumesManager, nil 27 | } 28 | 29 | func (e *executor) createVolumesManager() error { 30 | vm, err := createVolumesManager(e) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | e.volumesManager = vm 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /executors/init.go: -------------------------------------------------------------------------------- 1 | package executors 2 | 3 | import ( 4 | // make sure that shells get loaded before executors 5 | // this happens, because of difference in ordering init() 6 | // from external packages between 1.4.x and 1.5.x 7 | // this import forces to load shells before 8 | // and fixes: panic: no shells defined 9 | _ "gitlab.com/gitlab-org/gitlab-runner/shells" 10 | ) 11 | -------------------------------------------------------------------------------- /executors/kubernetes/helpers_kubernetes_test.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | restclient "k8s.io/client-go/rest" 5 | 6 | "gitlab.com/gitlab-org/gitlab-runner/common" 7 | "gitlab.com/gitlab-org/gitlab-runner/executors" 8 | ) 9 | 10 | // GetKubeClientConfig is used to export the getKubeClientConfig function for integration tests 11 | func GetKubeClientConfig(config *common.KubernetesConfig) (kubeConfig *restclient.Config, err error) { 12 | return getKubeClientConfig(config, new(overwrites)) 13 | } 14 | 15 | // NewDefaultExecutorForTest is used to expose the executor to integration tests 16 | func NewDefaultExecutorForTest() common.Executor { 17 | return &executor{ 18 | AbstractExecutor: executors.AbstractExecutor{ 19 | ExecutorOptions: executorOptions, 20 | }, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /executors/kubernetes/internal/pull/errors.go: -------------------------------------------------------------------------------- 1 | package pull 2 | 3 | import "fmt" 4 | 5 | // compile-time assertion to ensure ImagePullError always implements the 6 | // error interface 7 | var _ error = &ImagePullError{} 8 | 9 | type ImagePullError struct { 10 | Message string 11 | Image string 12 | } 13 | 14 | func (e *ImagePullError) Error() string { 15 | return fmt.Sprintf("pulling image %q: %s", e.Image, e.Message) 16 | } 17 | -------------------------------------------------------------------------------- /executors/kubernetes/internal/pull/mock_pullLogger.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package pull 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockPullLogger is an autogenerated mock type for the pullLogger type 8 | type mockPullLogger struct { 9 | mock.Mock 10 | } 11 | 12 | // Infoln provides a mock function with given fields: args 13 | func (_m *mockPullLogger) Infoln(args ...interface{}) { 14 | var _ca []interface{} 15 | _ca = append(_ca, args...) 16 | _m.Called(_ca...) 17 | } 18 | 19 | // Warningln provides a mock function with given fields: args 20 | func (_m *mockPullLogger) Warningln(args ...interface{}) { 21 | var _ca []interface{} 22 | _ca = append(_ca, args...) 23 | _m.Called(_ca...) 24 | } 25 | -------------------------------------------------------------------------------- /executors/kubernetes/mock_RemoteExecutor.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package kubernetes 4 | 5 | import ( 6 | io "io" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | rest "k8s.io/client-go/rest" 10 | 11 | url "net/url" 12 | ) 13 | 14 | // MockRemoteExecutor is an autogenerated mock type for the RemoteExecutor type 15 | type MockRemoteExecutor struct { 16 | mock.Mock 17 | } 18 | 19 | // Execute provides a mock function with given fields: method, _a1, config, stdin, stdout, stderr, tty 20 | func (_m *MockRemoteExecutor) Execute(method string, _a1 *url.URL, config *rest.Config, stdin io.Reader, stdout io.Writer, stderr io.Writer, tty bool) error { 21 | ret := _m.Called(method, _a1, config, stdin, stdout, stderr, tty) 22 | 23 | var r0 error 24 | if rf, ok := ret.Get(0).(func(string, *url.URL, *rest.Config, io.Reader, io.Writer, io.Writer, bool) error); ok { 25 | r0 = rf(method, _a1, config, stdin, stdout, stderr, tty) 26 | } else { 27 | r0 = ret.Error(0) 28 | } 29 | 30 | return r0 31 | } 32 | -------------------------------------------------------------------------------- /executors/kubernetes/mock_backoffCalculator.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package kubernetes 4 | 5 | import ( 6 | time "time" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // mockBackoffCalculator is an autogenerated mock type for the backoffCalculator type 12 | type mockBackoffCalculator struct { 13 | mock.Mock 14 | } 15 | 16 | // ForAttempt provides a mock function with given fields: attempt 17 | func (_m *mockBackoffCalculator) ForAttempt(attempt float64) time.Duration { 18 | ret := _m.Called(attempt) 19 | 20 | var r0 time.Duration 21 | if rf, ok := ret.Get(0).(func(float64) time.Duration); ok { 22 | r0 = rf(attempt) 23 | } else { 24 | r0 = ret.Get(0).(time.Duration) 25 | } 26 | 27 | return r0 28 | } 29 | -------------------------------------------------------------------------------- /executors/kubernetes/mock_featureChecker.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package kubernetes 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockFeatureChecker is an autogenerated mock type for the featureChecker type 8 | type mockFeatureChecker struct { 9 | mock.Mock 10 | } 11 | 12 | // IsHostAliasSupported provides a mock function with given fields: 13 | func (_m *mockFeatureChecker) IsHostAliasSupported() (bool, error) { 14 | ret := _m.Called() 15 | 16 | var r0 bool 17 | if rf, ok := ret.Get(0).(func() bool); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Get(0).(bool) 21 | } 22 | 23 | var r1 error 24 | if rf, ok := ret.Get(1).(func() error); ok { 25 | r1 = rf() 26 | } else { 27 | r1 = ret.Error(1) 28 | } 29 | 30 | return r0, r1 31 | } 32 | -------------------------------------------------------------------------------- /executors/kubernetes/mock_logProcessor.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package kubernetes 4 | 5 | import ( 6 | context "context" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // mockLogProcessor is an autogenerated mock type for the logProcessor type 12 | type mockLogProcessor struct { 13 | mock.Mock 14 | } 15 | 16 | // Process provides a mock function with given fields: ctx 17 | func (_m *mockLogProcessor) Process(ctx context.Context) (<-chan string, <-chan error) { 18 | ret := _m.Called(ctx) 19 | 20 | var r0 <-chan string 21 | if rf, ok := ret.Get(0).(func(context.Context) <-chan string); ok { 22 | r0 = rf(ctx) 23 | } else { 24 | if ret.Get(0) != nil { 25 | r0 = ret.Get(0).(<-chan string) 26 | } 27 | } 28 | 29 | var r1 <-chan error 30 | if rf, ok := ret.Get(1).(func(context.Context) <-chan error); ok { 31 | r1 = rf(ctx) 32 | } else { 33 | if ret.Get(1) != nil { 34 | r1 = ret.Get(1).(<-chan error) 35 | } 36 | } 37 | 38 | return r0, r1 39 | } 40 | -------------------------------------------------------------------------------- /executors/kubernetes/mock_logStreamer.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package kubernetes 4 | 5 | import ( 6 | io "io" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // mockLogStreamer is an autogenerated mock type for the logStreamer type 12 | type mockLogStreamer struct { 13 | mock.Mock 14 | } 15 | 16 | // Stream provides a mock function with given fields: offset, output 17 | func (_m *mockLogStreamer) Stream(offset int64, output io.Writer) error { 18 | ret := _m.Called(offset, output) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(int64, io.Writer) error); ok { 22 | r0 = rf(offset, output) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | 30 | // String provides a mock function with given fields: 31 | func (_m *mockLogStreamer) String() string { 32 | ret := _m.Called() 33 | 34 | var r0 string 35 | if rf, ok := ret.Get(0).(func() string); ok { 36 | r0 = rf() 37 | } else { 38 | r0 = ret.Get(0).(string) 39 | } 40 | 41 | return r0 42 | } 43 | -------------------------------------------------------------------------------- /executors/parallels/parallels_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package parallels 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | 11 | "gitlab.com/gitlab-org/gitlab-runner/common" 12 | ) 13 | 14 | func TestParallelsExecutorRegistered(t *testing.T) { 15 | executorNames := common.GetExecutorNames() 16 | assert.Contains(t, executorNames, "parallels") 17 | } 18 | 19 | func TestParallelsCreateExecutor(t *testing.T) { 20 | executor := common.NewExecutor("parallels") 21 | assert.NotNil(t, executor) 22 | } 23 | -------------------------------------------------------------------------------- /executors/virtualbox/virtualbox_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package virtualbox 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | 11 | "gitlab.com/gitlab-org/gitlab-runner/common" 12 | ) 13 | 14 | func TestVirtualBoxExecutorRegistered(t *testing.T) { 15 | executorNames := common.GetExecutorNames() 16 | assert.Contains(t, executorNames, "virtualbox") 17 | } 18 | 19 | func TestVirtualBoxCreateExecutor(t *testing.T) { 20 | executor := common.NewExecutor("virtualbox") 21 | assert.NotNil(t, executor) 22 | } 23 | -------------------------------------------------------------------------------- /helpers/ansi_colors.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | const ( 4 | ANSI_BOLD_BLACK = "\033[30;1m" 5 | ANSI_BOLD_RED = "\033[31;1m" 6 | ANSI_BOLD_GREEN = "\033[32;1m" 7 | ANSI_BOLD_YELLOW = "\033[33;1m" 8 | ANSI_BOLD_BLUE = "\033[34;1m" 9 | ANSI_BOLD_MAGENTA = "\033[35;1m" 10 | ANSI_BOLD_CYAN = "\033[36;1m" 11 | ANSI_BOLD_WHITE = "\033[37;1m" 12 | ANSI_YELLOW = "\033[0;33m" 13 | ANSI_RESET = "\033[0;m" 14 | ANSI_CLEAR = "\033[0K" 15 | ) 16 | -------------------------------------------------------------------------------- /helpers/archives/path_check_helper.go: -------------------------------------------------------------------------------- 1 | package archives 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/sirupsen/logrus" 11 | ) 12 | 13 | func isPathAGitDirectory(path string) bool { 14 | parts := strings.Split(filepath.Clean(path), string(filepath.Separator)) 15 | if len(parts) > 0 && parts[0] == ".git" { 16 | return true 17 | } 18 | return false 19 | } 20 | 21 | func errorIfGitDirectory(path string) *os.PathError { 22 | if !isPathAGitDirectory(path) { 23 | return nil 24 | } 25 | 26 | return &os.PathError{ 27 | Op: ".git inside of archive", 28 | Path: path, 29 | Err: errors.New("trying to archive or extract .git path"), 30 | } 31 | } 32 | 33 | func printGitArchiveWarning(operation string) { 34 | logrus.Warn(fmt.Sprintf("Part of .git directory is on the list of files to %s", operation)) 35 | logrus.Warn("This may introduce unexpected problems") 36 | } 37 | -------------------------------------------------------------------------------- /helpers/archives/path_check_helper_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package archives 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestDoesPathsListContainGitDirectory(t *testing.T) { 14 | examples := []struct { 15 | path string 16 | unsafe bool 17 | }{ 18 | {".git", true}, 19 | {".git/", true}, 20 | {"././././.git/", true}, 21 | {"././.git/.././.git/", true}, 22 | {".git/test", true}, 23 | {"./.git/test", true}, 24 | {"test/.git", false}, 25 | {"test/.git/test", false}, 26 | } 27 | 28 | for id, example := range examples { 29 | t.Run(fmt.Sprintf("example-%d", id), func(t *testing.T) { 30 | unsafe := isPathAGitDirectory(example.path) 31 | assert.Equal(t, example.unsafe, unsafe) 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /helpers/archives/path_error_tracker.go: -------------------------------------------------------------------------------- 1 | package archives 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | ) 7 | 8 | // When extracting an archive, the same PathError.Op may be repeated for every 9 | // file in the archive; use pathErrorTracker to suppress repetitious log output 10 | type pathErrorTracker struct { 11 | lock sync.Mutex 12 | seenOps map[string]bool 13 | } 14 | 15 | // check whether the error is actionable, which is to say, not nil and either 16 | // not a PathError, or a novel PathError 17 | func (p *pathErrorTracker) actionable(e error) bool { 18 | pathErr, isPathErr := e.(*os.PathError) 19 | if e == nil || isPathErr && pathErr == nil { 20 | return false 21 | } 22 | 23 | if !isPathErr { 24 | return true 25 | } 26 | 27 | p.lock.Lock() 28 | defer p.lock.Unlock() 29 | 30 | seen := p.seenOps[pathErr.Op] 31 | p.seenOps[pathErr.Op] = true 32 | 33 | // actionable if *not* seen before 34 | return !seen 35 | } 36 | 37 | func newPathErrorTracker() *pathErrorTracker { 38 | return &pathErrorTracker{ 39 | seenOps: make(map[string]bool), 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /helpers/archives/zip_create_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration && !windows 2 | // +build !integration,!windows 3 | 4 | package archives 5 | 6 | import ( 7 | "syscall" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func createTestPipe(t *testing.T, csb charsetByte) string { 14 | name := "test_pipe" 15 | if csb == multiBytes { 16 | name = "テストパイプ" 17 | } 18 | 19 | err := syscall.Mkfifo(name, 0600) 20 | assert.NoError(t, err) 21 | return name 22 | } 23 | -------------------------------------------------------------------------------- /helpers/archives/zip_create_windows_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration && windows 2 | // +build !integration,windows 3 | 4 | package archives 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func createTestPipe(t *testing.T, csb charsetByte) string { 11 | panic("unsupported - this should not be called") 12 | } 13 | -------------------------------------------------------------------------------- /helpers/archives/zip_extra_windows.go: -------------------------------------------------------------------------------- 1 | package archives 2 | 3 | import ( 4 | "archive/zip" 5 | "io" 6 | "os" 7 | ) 8 | 9 | func createZipUIDGidField(w io.Writer, fi os.FileInfo) (err error) { 10 | // TODO: currently not supported 11 | return nil 12 | } 13 | 14 | func processZipUIDGidField(data []byte, file *zip.FileHeader) error { 15 | // TODO: currently not supported 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /helpers/build_section.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type RawLogger interface { 9 | SendRawLog(args ...interface{}) 10 | } 11 | 12 | type BuildSection struct { 13 | Name string 14 | SkipMetrics bool 15 | Run func() error 16 | } 17 | 18 | const ( 19 | traceSectionStart = "section_start:%v:%s\r" + ANSI_CLEAR 20 | traceSectionEnd = "section_end:%v:%s\r" + ANSI_CLEAR 21 | ) 22 | 23 | func nowUnixUTC() int64 { 24 | return time.Now().UTC().Unix() 25 | } 26 | 27 | func (s *BuildSection) timestamp(format string, logger RawLogger) { 28 | if s.SkipMetrics { 29 | return 30 | } 31 | 32 | sectionLine := fmt.Sprintf(format, nowUnixUTC(), s.Name) 33 | logger.SendRawLog(sectionLine) 34 | } 35 | 36 | func (s *BuildSection) start(logger RawLogger) { 37 | s.timestamp(traceSectionStart, logger) 38 | } 39 | 40 | func (s *BuildSection) end(logger RawLogger) { 41 | s.timestamp(traceSectionEnd, logger) 42 | } 43 | 44 | func (s *BuildSection) Execute(logger RawLogger) error { 45 | s.start(logger) 46 | defer s.end(logger) 47 | 48 | return s.Run() 49 | } 50 | -------------------------------------------------------------------------------- /helpers/certificate/certificate.go: -------------------------------------------------------------------------------- 1 | package certificate 2 | 3 | import "crypto/tls" 4 | 5 | type Generator interface { 6 | Generate(host string) (tls.Certificate, []byte, error) 7 | } 8 | -------------------------------------------------------------------------------- /helpers/certificate/mock_Generator.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package certificate 4 | 5 | import ( 6 | tls "crypto/tls" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockGenerator is an autogenerated mock type for the Generator type 12 | type MockGenerator struct { 13 | mock.Mock 14 | } 15 | 16 | // Generate provides a mock function with given fields: host 17 | func (_m *MockGenerator) Generate(host string) (tls.Certificate, []byte, error) { 18 | ret := _m.Called(host) 19 | 20 | var r0 tls.Certificate 21 | if rf, ok := ret.Get(0).(func(string) tls.Certificate); ok { 22 | r0 = rf(host) 23 | } else { 24 | r0 = ret.Get(0).(tls.Certificate) 25 | } 26 | 27 | var r1 []byte 28 | if rf, ok := ret.Get(1).(func(string) []byte); ok { 29 | r1 = rf(host) 30 | } else { 31 | if ret.Get(1) != nil { 32 | r1 = ret.Get(1).([]byte) 33 | } 34 | } 35 | 36 | var r2 error 37 | if rf, ok := ret.Get(2).(func(string) error); ok { 38 | r2 = rf(host) 39 | } else { 40 | r2 = ret.Error(2) 41 | } 42 | 43 | return r0, r1, r2 44 | } 45 | -------------------------------------------------------------------------------- /helpers/cli/cpuprofile.go: -------------------------------------------------------------------------------- 1 | package cli_helpers 2 | 3 | import ( 4 | "os" 5 | "runtime/pprof" 6 | 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | func SetupCPUProfile(app *cli.App) { 11 | app.Flags = append(app.Flags, cli.StringFlag{ 12 | Name: "cpuprofile", 13 | Usage: "write cpu profile to file", 14 | EnvVar: "CPU_PROFILE", 15 | }) 16 | 17 | appBefore := app.Before 18 | appAfter := app.After 19 | 20 | app.Before = func(c *cli.Context) error { 21 | if cpuProfile := c.String("cpuprofile"); cpuProfile != "" { 22 | f, err := os.Create(cpuProfile) 23 | if err != nil { 24 | return err 25 | } 26 | _ = pprof.StartCPUProfile(f) 27 | } 28 | 29 | if appBefore != nil { 30 | return appBefore(c) 31 | } 32 | return nil 33 | } 34 | 35 | app.After = func(c *cli.Context) error { 36 | pprof.StopCPUProfile() 37 | 38 | if appAfter != nil { 39 | return appAfter(c) 40 | } 41 | return nil 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /helpers/cli/fix_home.go: -------------------------------------------------------------------------------- 1 | package cli_helpers 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/docker/docker/pkg/homedir" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | func FixHOME(app *cli.App) { 12 | appBefore := app.Before 13 | 14 | app.Before = func(c *cli.Context) error { 15 | // Fix home 16 | if key := homedir.Key(); os.Getenv(key) == "" { 17 | value := homedir.Get() 18 | if value == "" { 19 | return fmt.Errorf("the %q is not set", key) 20 | } 21 | _ = os.Setenv(key, value) 22 | } 23 | 24 | if appBefore != nil { 25 | return appBefore(c) 26 | } 27 | return nil 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /helpers/cli/init_cli.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package cli_helpers 5 | 6 | func InitCli() {} 7 | -------------------------------------------------------------------------------- /helpers/cli/init_cli_windows.go: -------------------------------------------------------------------------------- 1 | package cli_helpers 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "golang.org/x/sys/windows" 6 | ) 7 | 8 | // InitCli initializes the Windows console window by activating virtual terminal features. 9 | // Calling this function enables colored terminal output. 10 | func InitCli() { 11 | setConsoleMode(windows.Stdout, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) // enable VT processing on standard output stream 12 | setConsoleMode(windows.Stderr, windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) // enable VT processing on standard error stream 13 | } 14 | 15 | // setConsoleMode sets the given flags on the given 16 | // console standard stream. 17 | func setConsoleMode(handle windows.Handle, flags uint32) { 18 | var mode uint32 19 | 20 | // add console mode flag 21 | if err := windows.GetConsoleMode(handle, &mode); err == nil { 22 | err := windows.SetConsoleMode(handle, mode|flags) 23 | if err != nil { 24 | logrus.WithError(err).Info("Did not set console mode for cli") 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /helpers/cli/runtime_platform.go: -------------------------------------------------------------------------------- 1 | package cli_helpers 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | 7 | "github.com/sirupsen/logrus" 8 | "github.com/urfave/cli" 9 | 10 | "gitlab.com/gitlab-org/gitlab-runner/common" 11 | ) 12 | 13 | func LogRuntimePlatform(app *cli.App) { 14 | appBefore := app.Before 15 | app.Before = func(c *cli.Context) error { 16 | fields := logrus.Fields{ 17 | "os": runtime.GOOS, 18 | "arch": runtime.GOARCH, 19 | "version": common.VERSION, 20 | "revision": common.REVISION, 21 | "pid": os.Getpid(), 22 | } 23 | 24 | logrus.WithFields(fields).Info("Runtime platform") 25 | 26 | if appBefore != nil { 27 | return appBefore(c) 28 | } 29 | return nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /helpers/cli/warn_on_bool.go: -------------------------------------------------------------------------------- 1 | package cli_helpers 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/sirupsen/logrus" 7 | ) 8 | 9 | // WarnOnBool logs warning if args contains true or false 10 | // github.com/urfave/cli breaks badly if boolean are set using --flag true instead of --flag=true or just --flag 11 | // this is a simple check that warn the user about this if detects "true" or "false" alone in the arguments 12 | func WarnOnBool(args []string) { 13 | // we skip the first element because it contains the program name 14 | for idx, a := range args[1:] { 15 | arg := strings.ToLower(a) 16 | if arg == "true" || arg == "false" { 17 | supposedFlag := "--key" 18 | if idx > 0 { 19 | supposedFlag = args[idx] 20 | } 21 | 22 | logrus.Warningf("boolean parameters must be passed in the command line with %s=%s", supposedFlag, arg) 23 | logrus.Warningln("parameters after this may be ignored") 24 | break 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /helpers/container/helperimage/mock_creator.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package helperimage 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockCreator is an autogenerated mock type for the creator type 8 | type mockCreator struct { 9 | mock.Mock 10 | } 11 | 12 | // Create provides a mock function with given fields: revision, cfg 13 | func (_m *mockCreator) Create(revision string, cfg Config) (Info, error) { 14 | ret := _m.Called(revision, cfg) 15 | 16 | var r0 Info 17 | if rf, ok := ret.Get(0).(func(string, Config) Info); ok { 18 | r0 = rf(revision, cfg) 19 | } else { 20 | r0 = ret.Get(0).(Info) 21 | } 22 | 23 | var r1 error 24 | if rf, ok := ret.Get(1).(func(string, Config) error); ok { 25 | r1 = rf(revision, cfg) 26 | } else { 27 | r1 = ret.Error(1) 28 | } 29 | 30 | return r0, r1 31 | } 32 | -------------------------------------------------------------------------------- /helpers/dns/test/test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "regexp" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func AssertRFC1123Compatibility(t *testing.T, name string) { 11 | dns1123MaxLength := 63 12 | dns1123FormatRegexp := regexp.MustCompile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$") 13 | 14 | assert.True(t, len(name) <= dns1123MaxLength, "Name length needs to be shorter than %d", dns1123MaxLength) 15 | assert.Regexp(t, dns1123FormatRegexp, name, "Name needs to be in RFC-1123 allowed format") 16 | } 17 | -------------------------------------------------------------------------------- /helpers/docker/auth/testdata/docker-credential-bin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | case $1 in 4 | get) 5 | cat </dev/stderr 22 | esac 23 | -------------------------------------------------------------------------------- /helpers/docker/auth/testdata/docker-credential-windows.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | IF "%1"=="get" ( 3 | echo { 4 | echo "username": "script_user_1", 5 | echo "secret": "script_password_1", 6 | echo "serveraddress": "https://registry2.domain.tld:5005/v1/" 7 | echo } 8 | exit 9 | ) 10 | IF "%1"=="list" ( 11 | echo { 12 | echo "https://registry2.domain.tld:5005/v1/": "script_user_1" 13 | echo } 14 | exit 15 | ) 16 | -------------------------------------------------------------------------------- /helpers/docker/credentials.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | ) 7 | 8 | //nolint:lll 9 | type Credentials struct { 10 | Host string `toml:"host,omitempty" json:"host" long:"host" env:"DOCKER_HOST" description:"Docker daemon address"` 11 | CertPath string `toml:"tls_cert_path,omitempty" json:"tls_cert_path" long:"cert-path" env:"DOCKER_CERT_PATH" description:"Certificate path"` 12 | TLSVerify bool `toml:"tls_verify,omitzero" json:"tls_verify" long:"tlsverify" env:"DOCKER_TLS_VERIFY" description:"Use TLS and verify the remote"` 13 | } 14 | 15 | func credentialsFromEnv() Credentials { 16 | tlsVerify, _ := strconv.ParseBool(os.Getenv("DOCKER_TLS_VERIFY")) 17 | return Credentials{ 18 | Host: os.Getenv("DOCKER_HOST"), 19 | CertPath: os.Getenv("DOCKER_CERT_PATH"), 20 | TLSVerify: tlsVerify, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /helpers/docker/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // ErrOSNotSupported is used when docker does not support the detected OSType. 8 | // NewErrOSNotSupported is used to initialize this type. 9 | type ErrOSNotSupported struct { 10 | detectedOSType string 11 | } 12 | 13 | func (e *ErrOSNotSupported) Error() string { 14 | return fmt.Sprintf("unsupported OSType %q", e.detectedOSType) 15 | } 16 | 17 | func (e *ErrOSNotSupported) Is(err error) bool { 18 | _, ok := err.(*ErrOSNotSupported) 19 | 20 | return ok 21 | } 22 | 23 | // NewErrOSNotSupported creates a ErrOSNotSupported for the specified OSType. 24 | func NewErrOSNotSupported(osType string) *ErrOSNotSupported { 25 | return &ErrOSNotSupported{ 26 | detectedOSType: osType, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /helpers/docker/machine.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Machine interface { 8 | Create(driver, name string, opts ...string) error 9 | Provision(name string) error 10 | Remove(name string) error 11 | Stop(name string, timeout time.Duration) error 12 | List() (machines []string, err error) 13 | Exist(name string) bool 14 | 15 | CanConnect(name string, skipCache bool) bool 16 | Credentials(name string) (Credentials, error) 17 | } 18 | -------------------------------------------------------------------------------- /helpers/docker/test/error.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | // NotFoundError implements the interface that docker client checks for 4 | // `IsErrNotFound` 5 | // https://github.com/moby/moby/blob/f6a5ccf492e8eab969ffad8404117806b4a15a35/client/errors.go#L36-L49 6 | type NotFoundError struct { 7 | } 8 | 9 | func (e *NotFoundError) NotFound() bool { 10 | return true 11 | } 12 | 13 | func (e *NotFoundError) Error() string { 14 | return "not found" 15 | } 16 | -------------------------------------------------------------------------------- /helpers/fatal_panic.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | type fatalLogHook struct { 11 | output io.Writer 12 | } 13 | 14 | func (s *fatalLogHook) Levels() []logrus.Level { 15 | return []logrus.Level{ 16 | logrus.FatalLevel, 17 | } 18 | } 19 | 20 | func (s *fatalLogHook) Fire(e *logrus.Entry) error { 21 | _, _ = fmt.Fprintln(s.output, e.Message) 22 | 23 | panic(e) 24 | } 25 | 26 | func MakeFatalToPanic() func() { 27 | logger := logrus.StandardLogger() 28 | hooks := make(logrus.LevelHooks) 29 | 30 | hooks.Add(&fatalLogHook{output: logger.Out}) 31 | oldHooks := logger.ReplaceHooks(hooks) 32 | 33 | return func() { 34 | logger.ReplaceHooks(oldHooks) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /helpers/home_dir.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/docker/docker/pkg/homedir" 7 | ) 8 | 9 | func GetCurrentWorkingDirectory() string { 10 | dir, err := os.Getwd() 11 | if err == nil { 12 | return dir 13 | } 14 | return "" 15 | } 16 | 17 | func GetHomeDir() string { 18 | return homedir.Get() 19 | } 20 | -------------------------------------------------------------------------------- /helpers/integration_tests.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func SkipIntegrationTests(t *testing.T, cmd ...string) { 12 | if testing.Short() { 13 | t.Skip("Skipping long tests") 14 | } 15 | 16 | if len(cmd) == 0 { 17 | return 18 | } 19 | 20 | executable, err := exec.LookPath(cmd[0]) 21 | if err != nil { 22 | t.Skip(cmd[0], "doesn't exist", err) 23 | } 24 | 25 | if err := executeCommandSucceeded(executable, cmd[1:]); err != nil { 26 | assert.FailNow(t, "failed integration test command", "%q failed with error: %v", executable, err) 27 | } 28 | } 29 | 30 | // executeCommandSucceeded tests whether a particular command execution successfully 31 | // completes. If it does not, it returns the error produced. 32 | func executeCommandSucceeded(executable string, args []string) error { 33 | cmd := exec.Command(executable, args...) 34 | out, err := cmd.CombinedOutput() 35 | if err != nil { 36 | return fmt.Errorf("%w - %s", err, string(out)) 37 | } 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /helpers/limitwriter/limit_writer.go: -------------------------------------------------------------------------------- 1 | package limitwriter 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | ) 7 | 8 | var ErrWriteLimitExceeded = errors.New("write limit exceeded") 9 | 10 | type limitWriter struct { 11 | w io.Writer 12 | limit int64 13 | written int64 14 | } 15 | 16 | func New(w io.Writer, n int64) io.Writer { 17 | return &limitWriter{w: w, limit: n} 18 | } 19 | 20 | func (w *limitWriter) Write(p []byte) (n int, err error) { 21 | capacity := w.limit - w.written 22 | if capacity <= 0 { 23 | return 0, io.ErrShortWrite 24 | } 25 | 26 | if int64(len(p)) > capacity { 27 | n, err = w.w.Write(p[:capacity]) 28 | if err == nil { 29 | err = ErrWriteLimitExceeded 30 | } 31 | } else { 32 | n, err = w.w.Write(p) 33 | } 34 | 35 | if n < 0 { 36 | n = 0 37 | } 38 | w.written += int64(n) 39 | 40 | return n, err 41 | } 42 | -------------------------------------------------------------------------------- /helpers/mock_RawLogger.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package helpers 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockRawLogger is an autogenerated mock type for the RawLogger type 8 | type MockRawLogger struct { 9 | mock.Mock 10 | } 11 | 12 | // SendRawLog provides a mock function with given fields: args 13 | func (_m *MockRawLogger) SendRawLog(args ...interface{}) { 14 | var _ca []interface{} 15 | _ca = append(_ca, args...) 16 | _m.Called(_ca...) 17 | } 18 | -------------------------------------------------------------------------------- /helpers/path.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import "strings" 4 | 5 | func ToBackslash(path string) string { 6 | return strings.ReplaceAll(path, "/", "\\") 7 | } 8 | 9 | func ToSlash(path string) string { 10 | return strings.ReplaceAll(path, "\\", "/") 11 | } 12 | -------------------------------------------------------------------------------- /helpers/path/unix_path.go: -------------------------------------------------------------------------------- 1 | package path 2 | 3 | import "path" 4 | 5 | type unixPath struct{} 6 | 7 | func (p *unixPath) Join(elem ...string) string { 8 | return path.Join(elem...) 9 | } 10 | 11 | func (p *unixPath) IsAbs(pathname string) bool { 12 | return path.IsAbs(pathname) 13 | } 14 | 15 | func (p *unixPath) IsRoot(pathname string) bool { 16 | pathname = path.Clean(pathname) 17 | return path.IsAbs(pathname) && path.Dir(pathname) == pathname 18 | } 19 | 20 | func (p *unixPath) Contains(basePath, targetPath string) bool { 21 | basePath = path.Clean(basePath) 22 | targetPath = path.Clean(targetPath) 23 | 24 | for { 25 | if targetPath == basePath { 26 | return true 27 | } 28 | if p.IsRoot(targetPath) || targetPath == "." { 29 | return false 30 | } 31 | targetPath = path.Dir(targetPath) 32 | } 33 | } 34 | 35 | //revive:disable:unexported-return 36 | func NewUnixPath() *unixPath { 37 | return &unixPath{} 38 | } 39 | -------------------------------------------------------------------------------- /helpers/path_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package helpers 5 | 6 | import "testing" 7 | 8 | func TestToBackslash(t *testing.T) { 9 | result := ToBackslash("smb://user/me/directory") 10 | expected := "smb:\\\\user\\me\\directory" 11 | 12 | if result != expected { 13 | t.Error("Expected", expected, ", got ", result) 14 | } 15 | } 16 | 17 | func TestToSlash(t *testing.T) { 18 | result := ToSlash("smb:\\\\user\\me\\directory") 19 | expected := "smb://user/me/directory" 20 | 21 | if result != expected { 22 | t.Error("Expected", expected, ", got ", result) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /helpers/process/commander_unix_test.go: -------------------------------------------------------------------------------- 1 | //nolint:lll 2 | //go:build !integration && (aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris) 3 | // +build !integration 4 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 5 | 6 | package process 7 | 8 | import ( 9 | "os/exec" 10 | "syscall" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func Test_cmd_Start(t *testing.T) { 18 | c := osCmd{ 19 | internal: &exec.Cmd{ 20 | SysProcAttr: &syscall.SysProcAttr{ 21 | Setpgid: false, 22 | }, 23 | }, 24 | } 25 | require.False(t, c.internal.SysProcAttr.Setpgid) 26 | _ = c.Start() 27 | assert.True(t, c.internal.SysProcAttr.Setpgid) 28 | } 29 | -------------------------------------------------------------------------------- /helpers/process/group_unix.go: -------------------------------------------------------------------------------- 1 | //go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris 2 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 3 | 4 | package process 5 | 6 | import ( 7 | "os/exec" 8 | "syscall" 9 | ) 10 | 11 | func setProcessGroup(c *exec.Cmd, _ bool) { 12 | c.SysProcAttr = &syscall.SysProcAttr{ 13 | Setpgid: true, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /helpers/process/group_unix_test.go: -------------------------------------------------------------------------------- 1 | //nolint:lll 2 | //go:build !integration && (aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris) 3 | // +build !integration 4 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 5 | 6 | package process 7 | 8 | import ( 9 | "fmt" 10 | "os/exec" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestSetProcessGroup(t *testing.T) { 18 | for _, pg := range []bool{true, false} { 19 | t.Run(fmt.Sprintf("process_%t", pg), func(t *testing.T) { 20 | cmd := exec.Command("sleep", "1") 21 | require.Nil(t, cmd.SysProcAttr) 22 | setProcessGroup(cmd, pg) 23 | assert.True(t, cmd.SysProcAttr.Setpgid) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /helpers/process/group_windows.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "os/exec" 5 | "syscall" 6 | ) 7 | 8 | func setProcessGroup(c *exec.Cmd, useLegacyStrategy bool) { 9 | if useLegacyStrategy { 10 | return 11 | } 12 | 13 | c.SysProcAttr = &syscall.SysProcAttr{ 14 | CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /helpers/process/group_windows_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package process 5 | 6 | import ( 7 | "os/exec" 8 | "syscall" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestSetProcessGroup(t *testing.T) { 16 | tests := map[string]bool{ 17 | "legacy process feature flag enabled": true, 18 | "legacy process feature flag disabled": false, 19 | } 20 | 21 | for tn, featureEnabled := range tests { 22 | t.Run(tn, func(t *testing.T) { 23 | cmd := exec.Command("sleep", "1") 24 | 25 | require.Nil(t, cmd.SysProcAttr) 26 | setProcessGroup(cmd, featureEnabled) 27 | 28 | if featureEnabled { 29 | require.Nil(t, cmd.SysProcAttr) 30 | } else { 31 | assert.Equal(t, uint32(syscall.CREATE_NEW_PROCESS_GROUP), cmd.SysProcAttr.CreationFlags) 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /helpers/process/helpers_killer_test.go: -------------------------------------------------------------------------------- 1 | // Helper functions that are shared between unit tests and integration tests 2 | 3 | package process 4 | 5 | // Killer is used to the killer interface to the integration tests package 6 | type Killer interface { 7 | killer 8 | } 9 | 10 | // NewKillerForTest is used to expose a new killer to the integration tests package 11 | func NewKillerForTest(logger Logger, cmd Commander) Killer { 12 | return newKiller(logger, cmd) 13 | } 14 | -------------------------------------------------------------------------------- /helpers/process/killer_unix_integration_test.go: -------------------------------------------------------------------------------- 1 | //nolint:lll 2 | //go:build integration && (aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris) 3 | // +build integration 4 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 5 | 6 | package process_test 7 | 8 | // Cases for UNIX systems that are used in `killer_test.go#TestKiller`. 9 | func testKillerTestCases() map[string]testKillerTestCase { 10 | return map[string]testKillerTestCase{ 11 | "command terminated": { 12 | alreadyStopped: false, 13 | skipTerminate: true, 14 | expectedError: "", 15 | }, 16 | "command not terminated": { 17 | alreadyStopped: false, 18 | skipTerminate: false, 19 | expectedError: "exit status 1", 20 | }, 21 | "command already stopped": { 22 | alreadyStopped: true, 23 | expectedError: "signal: killed", 24 | }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /helpers/process/killer_unix_test.go: -------------------------------------------------------------------------------- 1 | //nolint:lll 2 | //go:build !integration && (aix || android || darwin || dragonfly || freebsd || hurd || illumos || linux || netbsd || openbsd || solaris) 3 | // +build !integration 4 | // +build aix android darwin dragonfly freebsd hurd illumos linux netbsd openbsd solaris 5 | 6 | package process 7 | 8 | import ( 9 | "os" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func Test_unixKiller_getPID(t *testing.T) { 16 | mCmd := new(MockCommander) 17 | defer mCmd.AssertExpectations(t) 18 | mLogger := new(MockLogger) 19 | defer mLogger.AssertExpectations(t) 20 | 21 | killer := unixKiller{logger: mLogger, cmd: mCmd} 22 | 23 | mCmd.On("Process").Return(&os.Process{Pid: 1}).Once() 24 | 25 | pid := killer.getPID() 26 | assert.Equal(t, -1, pid) 27 | } 28 | -------------------------------------------------------------------------------- /helpers/process/killer_windows_integration_test.go: -------------------------------------------------------------------------------- 1 | //go:build integration && windows 2 | // +build integration,windows 3 | 4 | package process_test 5 | 6 | // Cases for Windows that are used in `filler.go#TestKiller`. 7 | func testKillerTestCases() map[string]testKillerTestCase { 8 | return map[string]testKillerTestCase{ 9 | "command terminated": { 10 | alreadyStopped: false, 11 | skipTerminate: true, 12 | expectedError: "exit status 1", 13 | }, 14 | "command not terminated": { 15 | alreadyStopped: false, 16 | skipTerminate: false, 17 | expectedError: "exit status 1", 18 | }, 19 | "command already stopped": { 20 | alreadyStopped: true, 21 | expectedError: "exit status 1", 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /helpers/process/logger.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | ) 6 | 7 | type Logger interface { 8 | WithFields(fields logrus.Fields) Logger 9 | Warn(args ...interface{}) 10 | } 11 | -------------------------------------------------------------------------------- /helpers/process/mock_KillWaiter.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package process 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockKillWaiter is an autogenerated mock type for the KillWaiter type 8 | type MockKillWaiter struct { 9 | mock.Mock 10 | } 11 | 12 | // KillAndWait provides a mock function with given fields: command, waitCh 13 | func (_m *MockKillWaiter) KillAndWait(command Commander, waitCh chan error) error { 14 | ret := _m.Called(command, waitCh) 15 | 16 | var r0 error 17 | if rf, ok := ret.Get(0).(func(Commander, chan error) error); ok { 18 | r0 = rf(command, waitCh) 19 | } else { 20 | r0 = ret.Error(0) 21 | } 22 | 23 | return r0 24 | } 25 | -------------------------------------------------------------------------------- /helpers/process/mock_Logger.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package process 4 | 5 | import ( 6 | logrus "github.com/sirupsen/logrus" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // MockLogger is an autogenerated mock type for the Logger type 11 | type MockLogger struct { 12 | mock.Mock 13 | } 14 | 15 | // Warn provides a mock function with given fields: args 16 | func (_m *MockLogger) Warn(args ...interface{}) { 17 | var _ca []interface{} 18 | _ca = append(_ca, args...) 19 | _m.Called(_ca...) 20 | } 21 | 22 | // WithFields provides a mock function with given fields: fields 23 | func (_m *MockLogger) WithFields(fields logrus.Fields) Logger { 24 | ret := _m.Called(fields) 25 | 26 | var r0 Logger 27 | if rf, ok := ret.Get(0).(func(logrus.Fields) Logger); ok { 28 | r0 = rf(fields) 29 | } else { 30 | if ret.Get(0) != nil { 31 | r0 = ret.Get(0).(Logger) 32 | } 33 | } 34 | 35 | return r0 36 | } 37 | -------------------------------------------------------------------------------- /helpers/process/mock_killer.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package process 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockKiller is an autogenerated mock type for the killer type 8 | type mockKiller struct { 9 | mock.Mock 10 | } 11 | 12 | // ForceKill provides a mock function with given fields: 13 | func (_m *mockKiller) ForceKill() { 14 | _m.Called() 15 | } 16 | 17 | // Terminate provides a mock function with given fields: 18 | func (_m *mockKiller) Terminate() { 19 | _m.Called() 20 | } 21 | -------------------------------------------------------------------------------- /helpers/random_uuid.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/hex" 6 | ) 7 | 8 | func GenerateRandomUUID(length int) (string, error) { 9 | data := make([]byte, length) 10 | _, err := rand.Read(data) 11 | if err != nil { 12 | return "", err 13 | } 14 | 15 | return hex.EncodeToString(data), nil 16 | } 17 | -------------------------------------------------------------------------------- /helpers/retry/mock_Retryable.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package retry 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockRetryable is an autogenerated mock type for the Retryable type 8 | type MockRetryable struct { 9 | mock.Mock 10 | } 11 | 12 | // Run provides a mock function with given fields: 13 | func (_m *MockRetryable) Run() error { 14 | ret := _m.Called() 15 | 16 | var r0 error 17 | if rf, ok := ret.Get(0).(func() error); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Error(0) 21 | } 22 | 23 | return r0 24 | } 25 | 26 | // ShouldRetry provides a mock function with given fields: tries, err 27 | func (_m *MockRetryable) ShouldRetry(tries int, err error) bool { 28 | ret := _m.Called(tries, err) 29 | 30 | var r0 bool 31 | if rf, ok := ret.Get(0).(func(int, error) bool); ok { 32 | r0 = rf(tries, err) 33 | } else { 34 | r0 = ret.Get(0).(bool) 35 | } 36 | 37 | return r0 38 | } 39 | -------------------------------------------------------------------------------- /helpers/secrets/errors.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ResolvingUnsupportedSecretError struct { 8 | name string 9 | } 10 | 11 | func NewResolvingUnsupportedSecretError(name string) error { 12 | return &ResolvingUnsupportedSecretError{name: name} 13 | } 14 | 15 | func (e *ResolvingUnsupportedSecretError) Error() string { 16 | return fmt.Sprintf("trying to resolve unsupported secret: %s", e.name) 17 | } 18 | 19 | func (e *ResolvingUnsupportedSecretError) Is(err error) bool { 20 | customErr, ok := err.(*ResolvingUnsupportedSecretError) 21 | if !ok { 22 | return false 23 | } 24 | 25 | return customErr.name == e.name 26 | } 27 | -------------------------------------------------------------------------------- /helpers/secrets/errors_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package secrets 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestResolvingUnsupportedSecretError_Error(t *testing.T) { 13 | err := NewResolvingUnsupportedSecretError("test") 14 | assert.Equal(t, "trying to resolve unsupported secret: test", err.Error()) 15 | } 16 | 17 | func TestResolvingUnsupportedSecretError_Is(t *testing.T) { 18 | assert.ErrorIs( 19 | t, 20 | NewResolvingUnsupportedSecretError("expected"), 21 | NewResolvingUnsupportedSecretError("expected"), 22 | ) 23 | assert.NotErrorIs(t, NewResolvingUnsupportedSecretError("expected"), new(ResolvingUnsupportedSecretError)) 24 | assert.NotErrorIs(t, NewResolvingUnsupportedSecretError("expected"), assert.AnError) 25 | } 26 | -------------------------------------------------------------------------------- /helpers/sentry/log_hook_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package sentry 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func TestNewLogHook(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | dsn string 14 | wantErr bool 15 | }{ 16 | { 17 | name: "test old DSN format", 18 | dsn: "https://user:password@sentry.io/project/314", 19 | wantErr: false, 20 | }, 21 | { 22 | name: "test new DSN format with HTTP", 23 | dsn: "http://key@sentry.io/314", 24 | wantErr: false, 25 | }, 26 | { 27 | name: "test new DSN format with HTTPS", 28 | dsn: "https://key@sentry.io/314", 29 | wantErr: false, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | _, err := NewLogHook(tt.dsn) 35 | if (err != nil) != tt.wantErr { 36 | t.Errorf("NewLogHook() error = %v, wantErr %v", err, tt.wantErr) 37 | return 38 | } 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /helpers/service/mock_stopStarter.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package service_helpers 4 | 5 | import ( 6 | service "github.com/kardianos/service" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // mockStopStarter is an autogenerated mock type for the stopStarter type 11 | type mockStopStarter struct { 12 | mock.Mock 13 | } 14 | 15 | // Start provides a mock function with given fields: _a0 16 | func (_m *mockStopStarter) Start(_a0 service.Service) error { 17 | ret := _m.Called(_a0) 18 | 19 | var r0 error 20 | if rf, ok := ret.Get(0).(func(service.Service) error); ok { 21 | r0 = rf(_a0) 22 | } else { 23 | r0 = ret.Error(0) 24 | } 25 | 26 | return r0 27 | } 28 | 29 | // Stop provides a mock function with given fields: _a0 30 | func (_m *mockStopStarter) Stop(_a0 service.Service) error { 31 | ret := _m.Called(_a0) 32 | 33 | var r0 error 34 | if rf, ok := ret.Get(0).(func(service.Service) error); ok { 35 | r0 = rf(_a0) 36 | } else { 37 | r0 = ret.Error(0) 38 | } 39 | 40 | return r0 41 | } 42 | -------------------------------------------------------------------------------- /helpers/service/service_factory.go: -------------------------------------------------------------------------------- 1 | package service_helpers 2 | 3 | import ( 4 | "github.com/kardianos/service" 5 | "github.com/sirupsen/logrus" 6 | ) 7 | 8 | func New(i service.Interface, c *service.Config) (service.Service, error) { 9 | s, err := service.New(i, c) 10 | if err == service.ErrNoServiceSystemDetected { 11 | logrus.Warningln("No service system detected. Some features may not work!") 12 | 13 | return &SimpleService{ 14 | i: i, 15 | c: c, 16 | }, nil 17 | } 18 | return s, err 19 | } 20 | -------------------------------------------------------------------------------- /helpers/service/simple_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package service_helpers 5 | 6 | import ( 7 | "errors" 8 | "testing" 9 | 10 | "github.com/golang/mock/gomock" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | var errExample = errors.New("example error") 15 | 16 | func TestStart(t *testing.T) { 17 | ctrl := gomock.NewController(t) 18 | defer ctrl.Finish() 19 | 20 | mi := &mockStopStarter{} 21 | s := &SimpleService{i: mi} 22 | 23 | mi.On("Start", s).Return(errExample) 24 | 25 | err := s.Run() 26 | assert.Equal(t, errExample, err) 27 | mi.AssertExpectations(t) 28 | } 29 | -------------------------------------------------------------------------------- /helpers/shell_escape_legacy_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package helpers 5 | 6 | import ( 7 | "crypto/rand" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func BenchmarkShellEscape(b *testing.B) { 14 | data := make([]byte, 1024*1024) 15 | if _, err := rand.Read(data); err != nil { 16 | panic(err) 17 | } 18 | dataStr := string(data) 19 | 20 | for i := 0; i < b.N; i++ { 21 | ShellEscapeLegacy(dataStr) 22 | } 23 | } 24 | 25 | func TestShellEscapeLegacy(t *testing.T) { 26 | var tests = []struct { 27 | in string 28 | out string 29 | }{ 30 | {"standard string", "$'standard string'"}, 31 | {"+\t\n\r&", "$'+\\t\\n\\r&'"}, 32 | {"", "''"}, 33 | } 34 | 35 | for _, test := range tests { 36 | actual := ShellEscapeLegacy(test.in) 37 | assert.Equal(t, test.out, actual, "src=%v", test.in) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /helpers/shorten_token.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | func ShortenToken(token string) string { 8 | if len(token) < 8 { 9 | return token 10 | } 11 | 12 | if token[:2] == "GR" && len(token) >= 17 { 13 | // Token is prefixed with RUNNERS_TOKEN_PREFIX: GR (for Gitlab Runner) combined with the rotation 14 | // date decimal-to-hex-encoded. Let's add some more characters in order to compensate. 15 | if strings.IndexFunc(token[2:9], isInvalidPrefixRune) == -1 { 16 | return token[:17] 17 | } 18 | } 19 | return token[:8] 20 | } 21 | 22 | func isInvalidPrefixRune(r rune) bool { 23 | return (r < '0' || r > '9') && (r < 'A' || r > 'F') && (r < 'a' || r > 'f') 24 | } 25 | -------------------------------------------------------------------------------- /helpers/shorten_token_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package helpers 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func TestShortenToken(t *testing.T) { 11 | var tests = []struct { 12 | in string 13 | out string 14 | }{ 15 | {"short", "short"}, 16 | {"veryverylongtoken", "veryvery"}, 17 | {"GR1348941Z196cJVywzZpx_Ki_Cn2", "GR1348941Z196cJVy"}, 18 | {"GJ1348941Z196cJVywzZpx_Ki_Cn2", "GJ134894"}, 19 | {"GRveryverylongtoken", "GRveryve"}, 20 | } 21 | 22 | for _, test := range tests { 23 | actual := ShortenToken(test.in) 24 | if actual != test.out { 25 | t.Error("Expected ", test.out, ", get ", actual) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /helpers/ssh/consts.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | const sshRetryInterval = 3 4 | -------------------------------------------------------------------------------- /helpers/test/helpers.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "testing" 7 | ) 8 | 9 | const ( 10 | OSWindows = "windows" 11 | OSLinux = "linux" 12 | ) 13 | 14 | func SkipIfGitLabCI(t *testing.T) { 15 | _, ok := os.LookupEnv("CI") 16 | if ok { 17 | t.Skipf("Skipping test on CI builds: %s", t.Name()) 18 | } 19 | } 20 | 21 | func SkipIfGitLabCIOn(t *testing.T, os string) { 22 | if runtime.GOOS != os { 23 | return 24 | } 25 | 26 | SkipIfGitLabCI(t) 27 | } 28 | 29 | func SkipIfGitLabCIWithMessage(t *testing.T, msg string) { 30 | _, ok := os.LookupEnv("CI") 31 | if ok { 32 | t.Skipf("Skipping test on CI builds: %s - %s", t.Name(), msg) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /helpers/tls/ca_chain/mock_Builder.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package ca_chain 4 | 5 | import ( 6 | tls "crypto/tls" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockBuilder is an autogenerated mock type for the Builder type 12 | type MockBuilder struct { 13 | mock.Mock 14 | } 15 | 16 | // BuildChainFromTLSConnectionState provides a mock function with given fields: TLS 17 | func (_m *MockBuilder) BuildChainFromTLSConnectionState(TLS *tls.ConnectionState) error { 18 | ret := _m.Called(TLS) 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func(*tls.ConnectionState) error); ok { 22 | r0 = rf(TLS) 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | 30 | // String provides a mock function with given fields: 31 | func (_m *MockBuilder) String() string { 32 | ret := _m.Called() 33 | 34 | var r0 string 35 | if rf, ok := ret.Get(0).(func() string); ok { 36 | r0 = rf() 37 | } else { 38 | r0 = ret.Get(0).(string) 39 | } 40 | 41 | return r0 42 | } 43 | -------------------------------------------------------------------------------- /helpers/tls/ca_chain/mock_fetcher.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package ca_chain 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // mockFetcher is an autogenerated mock type for the fetcher type 8 | type mockFetcher struct { 9 | mock.Mock 10 | } 11 | 12 | // Fetch provides a mock function with given fields: url 13 | func (_m *mockFetcher) Fetch(url string) ([]byte, error) { 14 | ret := _m.Called(url) 15 | 16 | var r0 []byte 17 | if rf, ok := ret.Get(0).(func(string) []byte); ok { 18 | r0 = rf(url) 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).([]byte) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func(string) error); ok { 27 | r1 = rf(url) 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | -------------------------------------------------------------------------------- /helpers/tls/ca_chain/mock_resolver.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package ca_chain 4 | 5 | import ( 6 | x509 "crypto/x509" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // mockResolver is an autogenerated mock type for the resolver type 12 | type mockResolver struct { 13 | mock.Mock 14 | } 15 | 16 | // Resolve provides a mock function with given fields: certs 17 | func (_m *mockResolver) Resolve(certs []*x509.Certificate) ([]*x509.Certificate, error) { 18 | ret := _m.Called(certs) 19 | 20 | var r0 []*x509.Certificate 21 | if rf, ok := ret.Get(0).(func([]*x509.Certificate) []*x509.Certificate); ok { 22 | r0 = rf(certs) 23 | } else { 24 | if ret.Get(0) != nil { 25 | r0 = ret.Get(0).([]*x509.Certificate) 26 | } 27 | } 28 | 29 | var r1 error 30 | if rf, ok := ret.Get(1).(func([]*x509.Certificate) error); ok { 31 | r1 = rf(certs) 32 | } else { 33 | r1 = ret.Error(1) 34 | } 35 | 36 | return r0, r1 37 | } 38 | -------------------------------------------------------------------------------- /helpers/tls/ca_chain/resolver.go: -------------------------------------------------------------------------------- 1 | package ca_chain 2 | 3 | import ( 4 | "crypto/x509" 5 | ) 6 | 7 | type resolver interface { 8 | Resolve(certs []*x509.Certificate) ([]*x509.Certificate, error) 9 | } 10 | -------------------------------------------------------------------------------- /helpers/tls/consts.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | const ( 4 | VariableCAFile string = "CI_SERVER_TLS_CA_FILE" 5 | VariableCertFile string = "CI_SERVER_TLS_CERT_FILE" 6 | VariableKeyFile string = "CI_SERVER_TLS_KEY_FILE" 7 | ) 8 | -------------------------------------------------------------------------------- /helpers/toml_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package helpers 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/BurntSushi/toml" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestTOMLOmitEmpty(t *testing.T) { 15 | var config struct { 16 | Value int `toml:"value,omitzero"` 17 | } 18 | 19 | // This test is intended to test this not fixed problem: 20 | // https://github.com/chowey/toml/commit/8249b7bc958927e7a8b392f66adbe4d5ead737d9 21 | text := `Value=10` 22 | _, err := toml.Decode(text, &config) 23 | require.NoError(t, err) 24 | assert.Equal(t, 10, config.Value) 25 | } 26 | -------------------------------------------------------------------------------- /helpers/url/clean_url.go: -------------------------------------------------------------------------------- 1 | package url_helpers 2 | 3 | import "net/url" 4 | 5 | func CleanURL(value string) (ret string) { 6 | u, err := url.Parse(value) 7 | if err != nil { 8 | return 9 | } 10 | u.User = nil 11 | u.RawQuery = "" 12 | u.Fragment = "" 13 | return u.String() 14 | } 15 | -------------------------------------------------------------------------------- /helpers/url/clean_url_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package url_helpers 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestRemovingAllSensitiveData(t *testing.T) { 13 | url := CleanURL("https://user:password@gitlab.com/gitlab?key=value#fragment") 14 | assert.Equal(t, "https://gitlab.com/gitlab", url) 15 | } 16 | 17 | func TestInvalidURL(t *testing.T) { 18 | assert.Empty(t, CleanURL("://invalid URL")) 19 | } 20 | -------------------------------------------------------------------------------- /helpers/url/scrub_secrets.go: -------------------------------------------------------------------------------- 1 | package url_helpers 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | var scrubRegexp = regexp.MustCompile( 8 | `(?im)([\?&]((?:private|authenticity|rss)[\-_]token)|X-AMZ-Signature|X-AMZ-Credential|X-AMZ-Security-Token)=[^& ]*`, 9 | ) 10 | 11 | // ScrubSecrets replaces the content of any sensitive query string parameters 12 | // in a URL with `[FILTERED]` 13 | func ScrubSecrets(url string) string { 14 | return scrubRegexp.ReplaceAllString(url, "$1=[FILTERED]") 15 | } 16 | -------------------------------------------------------------------------------- /helpers/vault/auth.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | type AuthMethod interface { 4 | Name() string 5 | Authenticate(client Client) error 6 | Token() string 7 | } 8 | -------------------------------------------------------------------------------- /helpers/vault/auth_methods/registry.go: -------------------------------------------------------------------------------- 1 | package auth_methods 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gitlab.com/gitlab-org/gitlab-runner/helpers/vault" 7 | "gitlab.com/gitlab-org/gitlab-runner/helpers/vault/internal/registry" 8 | ) 9 | 10 | type Factory func(path string, data Data) (vault.AuthMethod, error) 11 | 12 | var factoriesRegistry = registry.New("auth method") 13 | 14 | func MustRegisterFactory(authName string, factory Factory) { 15 | err := factoriesRegistry.Register(authName, factory) 16 | if err != nil { 17 | panic(fmt.Sprintf("registering factory: %v", err)) 18 | } 19 | } 20 | 21 | func GetFactory(authName string) (Factory, error) { 22 | factory, err := factoriesRegistry.Get(authName) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return factory.(Factory), nil 28 | } 29 | -------------------------------------------------------------------------------- /helpers/vault/mock_Result.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package vault 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockResult is an autogenerated mock type for the Result type 8 | type MockResult struct { 9 | mock.Mock 10 | } 11 | 12 | // Data provides a mock function with given fields: 13 | func (_m *MockResult) Data() map[string]interface{} { 14 | ret := _m.Called() 15 | 16 | var r0 map[string]interface{} 17 | if rf, ok := ret.Get(0).(func() map[string]interface{}); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(map[string]interface{}) 22 | } 23 | } 24 | 25 | return r0 26 | } 27 | 28 | // TokenID provides a mock function with given fields: 29 | func (_m *MockResult) TokenID() (string, error) { 30 | ret := _m.Called() 31 | 32 | var r0 string 33 | if rf, ok := ret.Get(0).(func() string); ok { 34 | r0 = rf() 35 | } else { 36 | r0 = ret.Get(0).(string) 37 | } 38 | 39 | var r1 error 40 | if rf, ok := ret.Get(1).(func() error); ok { 41 | r1 = rf() 42 | } else { 43 | r1 = ret.Error(1) 44 | } 45 | 46 | return r0, r1 47 | } 48 | -------------------------------------------------------------------------------- /helpers/vault/mock_apiClientSys.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package vault 4 | 5 | import ( 6 | api "github.com/hashicorp/vault/api" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // mockApiClientSys is an autogenerated mock type for the apiClientSys type 11 | type mockApiClientSys struct { 12 | mock.Mock 13 | } 14 | 15 | // Health provides a mock function with given fields: 16 | func (_m *mockApiClientSys) Health() (*api.HealthResponse, error) { 17 | ret := _m.Called() 18 | 19 | var r0 *api.HealthResponse 20 | if rf, ok := ret.Get(0).(func() *api.HealthResponse); ok { 21 | r0 = rf() 22 | } else { 23 | if ret.Get(0) != nil { 24 | r0 = ret.Get(0).(*api.HealthResponse) 25 | } 26 | } 27 | 28 | var r1 error 29 | if rf, ok := ret.Get(1).(func() error); ok { 30 | r1 = rf() 31 | } else { 32 | r1 = ret.Error(1) 33 | } 34 | 35 | return r0, r1 36 | } 37 | -------------------------------------------------------------------------------- /helpers/vault/result.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/hashicorp/vault/api" 7 | ) 8 | 9 | type Result interface { 10 | Data() map[string]interface{} 11 | TokenID() (string, error) 12 | } 13 | 14 | var ErrNoResult = errors.New("no result from Vault") 15 | 16 | type secretResult struct { 17 | inner *api.Secret 18 | } 19 | 20 | func newResult(secret *api.Secret) Result { 21 | return &secretResult{ 22 | inner: secret, 23 | } 24 | } 25 | 26 | func (r *secretResult) Data() map[string]interface{} { 27 | if r.inner == nil { 28 | return nil 29 | } 30 | 31 | return r.inner.Data 32 | } 33 | 34 | func (r *secretResult) TokenID() (string, error) { 35 | if r.inner == nil { 36 | return "", ErrNoResult 37 | } 38 | 39 | return r.inner.TokenID() 40 | } 41 | -------------------------------------------------------------------------------- /helpers/vault/secret_engine.go: -------------------------------------------------------------------------------- 1 | package vault 2 | 3 | type SecretEngine interface { 4 | EngineName() string 5 | Get(path string) (map[string]interface{}, error) 6 | Put(path string, data map[string]interface{}) error 7 | Delete(path string) error 8 | } 9 | -------------------------------------------------------------------------------- /helpers/vault/secret_engines/registry.go: -------------------------------------------------------------------------------- 1 | package secret_engines 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gitlab.com/gitlab-org/gitlab-runner/helpers/vault" 7 | "gitlab.com/gitlab-org/gitlab-runner/helpers/vault/internal/registry" 8 | ) 9 | 10 | type Factory func(client vault.Client, path string) vault.SecretEngine 11 | 12 | var factoriesRegistry = registry.New("secret engine") 13 | 14 | func MustRegisterFactory(engineName string, factory Factory) { 15 | err := factoriesRegistry.Register(engineName, factory) 16 | if err != nil { 17 | panic(fmt.Sprintf("registering factory: %v", err)) 18 | } 19 | } 20 | 21 | func GetFactory(engineName string) (Factory, error) { 22 | factory, err := factoriesRegistry.Get(engineName) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return factory.(Factory), nil 28 | } 29 | -------------------------------------------------------------------------------- /helpers/vault/service/mock_Engine.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package service 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockEngine is an autogenerated mock type for the Engine type 8 | type MockEngine struct { 9 | mock.Mock 10 | } 11 | 12 | // EngineName provides a mock function with given fields: 13 | func (_m *MockEngine) EngineName() string { 14 | ret := _m.Called() 15 | 16 | var r0 string 17 | if rf, ok := ret.Get(0).(func() string); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Get(0).(string) 21 | } 22 | 23 | return r0 24 | } 25 | 26 | // EnginePath provides a mock function with given fields: 27 | func (_m *MockEngine) EnginePath() string { 28 | ret := _m.Called() 29 | 30 | var r0 string 31 | if rf, ok := ret.Get(0).(func() string); ok { 32 | r0 = rf() 33 | } else { 34 | r0 = ret.Get(0).(string) 35 | } 36 | 37 | return r0 38 | } 39 | -------------------------------------------------------------------------------- /helpers/vault/service/mock_Secret.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package service 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockSecret is an autogenerated mock type for the Secret type 8 | type MockSecret struct { 9 | mock.Mock 10 | } 11 | 12 | // SecretField provides a mock function with given fields: 13 | func (_m *MockSecret) SecretField() string { 14 | ret := _m.Called() 15 | 16 | var r0 string 17 | if rf, ok := ret.Get(0).(func() string); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Get(0).(string) 21 | } 22 | 23 | return r0 24 | } 25 | 26 | // SecretPath provides a mock function with given fields: 27 | func (_m *MockSecret) SecretPath() string { 28 | ret := _m.Called() 29 | 30 | var r0 string 31 | if rf, ok := ret.Get(0).(func() string); ok { 32 | r0 = rf() 33 | } else { 34 | r0 = ret.Get(0).(string) 35 | } 36 | 37 | return r0 38 | } 39 | -------------------------------------------------------------------------------- /helpers/virtualbox/control_windows.go: -------------------------------------------------------------------------------- 1 | package virtualbox 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | func init() { 12 | addDirectoryToPATH(os.Getenv("ProgramFiles")) 13 | addDirectoryToPATH(os.Getenv("ProgramFiles(X86)")) 14 | } 15 | 16 | func addDirectoryToPATH(programFilesPath string) { 17 | if programFilesPath == "" { 18 | return 19 | } 20 | 21 | virtualBoxPath := filepath.Join(programFilesPath, "Oracle", "VirtualBox") 22 | newPath := fmt.Sprintf("%s;%s", os.Getenv("PATH"), virtualBoxPath) 23 | err := os.Setenv("PATH", newPath) 24 | if err != nil { 25 | logrus.Warnf( 26 | "Failed to add path to VBoxManage.exe (%q) to end of local PATH: %v", 27 | virtualBoxPath, 28 | err) 29 | return 30 | } 31 | 32 | logrus.Debugf("Added path to VBoxManage.exe to end of local PATH: %q", virtualBoxPath) 33 | } 34 | -------------------------------------------------------------------------------- /helpers/warning_panic.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/sirupsen/logrus" 8 | ) 9 | 10 | type warningLogHook struct { 11 | output io.Writer 12 | } 13 | 14 | func (s *warningLogHook) Levels() []logrus.Level { 15 | return []logrus.Level{ 16 | logrus.WarnLevel, 17 | } 18 | } 19 | 20 | func (s *warningLogHook) Fire(e *logrus.Entry) error { 21 | _, _ = fmt.Fprintln(s.output, e.Message) 22 | 23 | panic(e) 24 | } 25 | 26 | func MakeWarningToPanic() func() { 27 | logger := logrus.StandardLogger() 28 | hooks := make(logrus.LevelHooks) 29 | 30 | hooks.Add(&warningLogHook{output: logger.Out}) 31 | oldHooks := logger.ReplaceHooks(hooks) 32 | 33 | return func() { 34 | logger.ReplaceHooks(oldHooks) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /log/dump_windows.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | ) 6 | 7 | func watchForGoroutinesDump(logger *logrus.Logger, stopCh chan bool) (chan bool, chan bool) { 8 | return nil, nil 9 | } 10 | -------------------------------------------------------------------------------- /log/secrets_cleanup.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | url_helpers "gitlab.com/gitlab-org/gitlab-runner/helpers/url" 6 | ) 7 | 8 | type SecretsCleanupHook struct{} 9 | 10 | func (s *SecretsCleanupHook) Levels() []logrus.Level { 11 | return logrus.AllLevels 12 | } 13 | 14 | func (s *SecretsCleanupHook) Fire(entry *logrus.Entry) error { 15 | entry.Message = url_helpers.ScrubSecrets(entry.Message) 16 | return nil 17 | } 18 | 19 | func AddSecretsCleanupLogHook(logger *logrus.Logger) { 20 | if logger == nil { 21 | logger = logrus.StandardLogger() 22 | } 23 | 24 | logger.AddHook(new(SecretsCleanupHook)) 25 | } 26 | -------------------------------------------------------------------------------- /log/secrets_cleanup_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package log 5 | 6 | import ( 7 | "bytes" 8 | "testing" 9 | 10 | "github.com/sirupsen/logrus" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestSecretsCleanupHook(t *testing.T) { 15 | tests := []struct { 16 | name string 17 | message string 18 | expected string 19 | }{ 20 | { 21 | name: "With Secrets", 22 | message: "Get http://localhost/?id=123&X-Amz-Signature=abcd1234&private_token=abcd1234", 23 | expected: "Get http://localhost/?id=123&X-Amz-Signature=[FILTERED]&private_token=[FILTERED]", 24 | }, 25 | { 26 | name: "No Secrets", 27 | message: "Fatal: Get http://localhost/?id=123", 28 | expected: "Fatal: Get http://localhost/?id=123", 29 | }, 30 | } 31 | 32 | for _, test := range tests { 33 | t.Run(test.name, func(t *testing.T) { 34 | buffer := &bytes.Buffer{} 35 | 36 | logger := logrus.New() 37 | logger.Out = buffer 38 | AddSecretsCleanupLogHook(logger) 39 | 40 | logger.Errorln(test.message) 41 | 42 | assert.Contains(t, buffer.String(), test.expected) 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /log/test/hook_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/sirupsen/logrus" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestNewHook(t *testing.T) { 14 | beforeCount := countHooks() 15 | 16 | _, cleanup := NewHook() 17 | afterCount := countHooks() 18 | 19 | cleanup() 20 | 21 | assert.True(t, afterCount > beforeCount) 22 | assert.Equal(t, beforeCount, countHooks()) 23 | } 24 | 25 | func countHooks() int { 26 | count := 0 27 | for _, levels := range logrus.StandardLogger().Hooks { 28 | for range levels { 29 | count++ 30 | } 31 | } 32 | 33 | return count 34 | } 35 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration || integration 2 | // +build !integration integration 3 | 4 | package main_test 5 | 6 | import ( 7 | "os" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | const failure = `Environment variables from GitLab detected in tests, 14 | these should be cleared: https://gitlab.com/gitlab-org/gitlab-runner/-/issues/27965` 15 | 16 | func TestEnvVariablesCleaned(t *testing.T) { 17 | assert.Empty(t, os.Getenv("CI_API_V4_URL"), failure) 18 | assert.NotEmpty(t, os.Getenv("CI"), "If running locally, use `export CI=0` explicitly.") 19 | } 20 | -------------------------------------------------------------------------------- /network/config.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | var CertificateDirectory string 4 | -------------------------------------------------------------------------------- /network/mock_requester.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package network 4 | 5 | import ( 6 | http "net/http" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // mockRequester is an autogenerated mock type for the requester type 12 | type mockRequester struct { 13 | mock.Mock 14 | } 15 | 16 | // Do provides a mock function with given fields: _a0 17 | func (_m *mockRequester) Do(_a0 *http.Request) (*http.Response, error) { 18 | ret := _m.Called(_a0) 19 | 20 | var r0 *http.Response 21 | if rf, ok := ret.Get(0).(func(*http.Request) *http.Response); ok { 22 | r0 = rf(_a0) 23 | } else { 24 | if ret.Get(0) != nil { 25 | r0 = ret.Get(0).(*http.Response) 26 | } 27 | } 28 | 29 | var r1 error 30 | if rf, ok := ret.Get(1).(func(*http.Request) error); ok { 31 | r1 = rf(_a0) 32 | } else { 33 | r1 = ret.Error(1) 34 | } 35 | 36 | return r0, r1 37 | } 38 | -------------------------------------------------------------------------------- /network/patch_response.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/sirupsen/logrus" 9 | ) 10 | 11 | const ( 12 | rangeHeader = "Range" 13 | ) 14 | 15 | type TracePatchResponse struct { 16 | *RemoteJobStateResponse 17 | 18 | RemoteRange string 19 | } 20 | 21 | func (p *TracePatchResponse) NewOffset() int { 22 | remoteRangeParts := strings.Split(p.RemoteRange, "-") 23 | if len(remoteRangeParts) == 2 { 24 | newOffset, _ := strconv.Atoi(remoteRangeParts[1]) 25 | return newOffset 26 | } 27 | 28 | return 0 29 | } 30 | 31 | func NewTracePatchResponse(response *http.Response, logger logrus.FieldLogger) *TracePatchResponse { 32 | result := &TracePatchResponse{ 33 | RemoteJobStateResponse: NewRemoteJobStateResponse(response, logger), 34 | } 35 | 36 | if response != nil { 37 | result.RemoteRange = response.Header.Get(rangeHeader) 38 | } 39 | 40 | return result 41 | } 42 | -------------------------------------------------------------------------------- /network/requester.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import "net/http" 4 | 5 | type requester interface { 6 | Do(*http.Request) (*http.Response, error) 7 | } 8 | -------------------------------------------------------------------------------- /packaging/root/usr/share/gitlab-runner/pre-remove: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gitlab-runner stop >/dev/null 2>/dev/null 4 | gitlab-runner uninstall >/dev/null 2>/dev/null 5 | exit 0 6 | -------------------------------------------------------------------------------- /packaging/scripts/postinst.deb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | case "$1" in 5 | abort-upgrade|abort-remove|abort-deconfigure) 6 | ;; 7 | 8 | configure) 9 | /usr/share/gitlab-runner/post-install 10 | /usr/share/gitlab-runner/clear-docker-cache || : 11 | ;; 12 | 13 | *) 14 | echo "postinst called with unknown argument \`$1'" >&2 15 | exit 1 16 | ;; 17 | esac 18 | 19 | exit 0 20 | -------------------------------------------------------------------------------- /packaging/scripts/postinst.rpm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | /usr/share/gitlab-runner/post-install 4 | /usr/share/gitlab-runner/clear-docker-cache || : 5 | exit 0 6 | -------------------------------------------------------------------------------- /packaging/scripts/prerm.deb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | /usr/share/gitlab-runner/pre-remove 4 | -------------------------------------------------------------------------------- /packaging/scripts/prerm.rpm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "x$1" = "x0" ]; then 4 | set -e 5 | /usr/share/gitlab-runner/pre-remove 6 | fi 7 | -------------------------------------------------------------------------------- /referees/mock_MetricsExecutor.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package referees 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockMetricsExecutor is an autogenerated mock type for the MetricsExecutor type 8 | type MockMetricsExecutor struct { 9 | mock.Mock 10 | } 11 | 12 | // GetMetricsSelector provides a mock function with given fields: 13 | func (_m *MockMetricsExecutor) GetMetricsSelector() string { 14 | ret := _m.Called() 15 | 16 | var r0 string 17 | if rf, ok := ret.Get(0).(func() string); ok { 18 | r0 = rf() 19 | } else { 20 | r0 = ret.Get(0).(string) 21 | } 22 | 23 | return r0 24 | } 25 | -------------------------------------------------------------------------------- /referees/mock_prometheusValue.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package referees 4 | 5 | import ( 6 | model "github.com/prometheus/common/model" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // mockPrometheusValue is an autogenerated mock type for the prometheusValue type 11 | type mockPrometheusValue struct { 12 | mock.Mock 13 | } 14 | 15 | // String provides a mock function with given fields: 16 | func (_m *mockPrometheusValue) String() string { 17 | ret := _m.Called() 18 | 19 | var r0 string 20 | if rf, ok := ret.Get(0).(func() string); ok { 21 | r0 = rf() 22 | } else { 23 | r0 = ret.Get(0).(string) 24 | } 25 | 26 | return r0 27 | } 28 | 29 | // Type provides a mock function with given fields: 30 | func (_m *mockPrometheusValue) Type() model.ValueType { 31 | ret := _m.Called() 32 | 33 | var r0 model.ValueType 34 | if rf, ok := ret.Get(0).(func() model.ValueType); ok { 35 | r0 = rf() 36 | } else { 37 | r0 = ret.Get(0).(model.ValueType) 38 | } 39 | 40 | return r0 41 | } 42 | -------------------------------------------------------------------------------- /referees/prometheus_api.go: -------------------------------------------------------------------------------- 1 | // The only purpose for having this files and interfaces redefined 2 | // in it is to make automatic mocks generator (`make mocks`) able to 3 | // create mocks of some Prometheus interfaces - which are not present 4 | // in the original packages but are required to make our tests simpler 5 | // and more "unit". 6 | 7 | package referees 8 | 9 | import ( 10 | prometheusV1 "github.com/prometheus/client_golang/api/prometheus/v1" 11 | "github.com/prometheus/common/model" 12 | ) 13 | 14 | //nolint:unused // see file header 15 | type prometheusAPI interface { 16 | prometheusV1.API 17 | } 18 | 19 | //nolint:unused // see file header 20 | type prometheusValue interface { 21 | model.Value 22 | } 23 | -------------------------------------------------------------------------------- /scripts/check_race_conditions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This is the number of race conditions detected on default branch. 4 | # This number is not allowed to increase, and it has to be lowered when we 5 | # fix existing race conditions 6 | max=24 7 | 8 | find .testoutput/ 9 | 10 | cnt=$(cat .testoutput/*/*.race.output.txt | grep -E "^WARNING: DATA RACE$" -c) 11 | echo "Found ${cnt} race conditions. Maximum allowed value is ${max}" 12 | 13 | if [ "${cnt}" -gt "${max}" ]; then 14 | echo "Race conditions count increased" 15 | exit 1 16 | fi 17 | -------------------------------------------------------------------------------- /scripts/check_test_directives: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ################################################################################################################ 4 | ## 5 | ## This script ensures that test file names match the required build tags, so that integration tests 6 | ## (which run for longer) can be run in a separate job from unit tests. 7 | ## - Integration test files (named *integration_*test.go) should have a '// +build integration' 8 | ## and '//go:build integration' build constraints 9 | ## - Unit test files (named *_test.go) should have a '// +build !integration' and '//go:build integration' 10 | ## build constraints 11 | ## 12 | ## Return value: 1 if any discrepancy is found, otherwise 0 13 | ## 14 | ################################################################################################################ 15 | 16 | set -o pipefail 17 | 18 | GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel) 19 | 20 | go run ${GIT_ROOT}/scripts/check-test-directives ${GIT_ROOT} 21 | -------------------------------------------------------------------------------- /scripts/envs/README.md: -------------------------------------------------------------------------------- 1 | List of environment variables that are allowed to be passed to tests. 2 | The variables are used in `go_test_no_env` and `go_test_no_env.ps1`. 3 | 4 | ### allowlist_common 5 | 6 | Variables shared between systems. Go-related variables are taken from running `go env`. 7 | Not running `go env` for simplicity and to avoid introducing variables with new go versions that we possibly shouldn't. 8 | 9 | ### allowlist_unix / allowlist_windows 10 | 11 | Only platform-specific env variables. 12 | -------------------------------------------------------------------------------- /scripts/envs/allowlist_common.env: -------------------------------------------------------------------------------- 1 | GO111MODULE 2 | GOARCH 3 | GOBIN 4 | GOCACHE 5 | GOENV 6 | GOEXE 7 | GOFLAGS 8 | GOHOSTARCH 9 | GOHOSTOS 10 | GOINSECURE 11 | GOMODCACHE 12 | GONOPROXY 13 | GONOSUMDB 14 | GOOS 15 | GOPATH 16 | GOPRIVATE 17 | GOPROXY 18 | GOROOT 19 | GOSUMDB 20 | GOTMPDIR 21 | GOTOOLDIR 22 | GOVERSION 23 | GOVCS 24 | GCCGO 25 | AR 26 | CC 27 | CXX 28 | CGO_ENABLED 29 | GOMOD 30 | CGO_CFLAGS 31 | CGO_CPPFLAGS 32 | CGO_CXXFLAGS 33 | CGO_FFLAGS 34 | CGO_LDFLAGS 35 | PKG_CONFIG 36 | GOGCCFLAGS 37 | 38 | DOCKER_HOST 39 | DOCKER_TLS_VERIFY 40 | DOCKER_TLS_CERTDIR 41 | DOCKER_CERT_PATH 42 | KUBECONFIG 43 | 44 | CI 45 | GITLAB_CI 46 | 47 | PATH 48 | -------------------------------------------------------------------------------- /scripts/envs/allowlist_unix.env: -------------------------------------------------------------------------------- 1 | HOME 2 | -------------------------------------------------------------------------------- /scripts/envs/allowlist_windows.env: -------------------------------------------------------------------------------- 1 | SystemRoot 2 | SystemDrive 3 | ProgramData 4 | ProgramFiles 5 | ProgramFiles(x86) 6 | ProgramW6432 7 | PSModulePath 8 | windir 9 | PUBLIC 10 | SESSIONNAME 11 | TEMP 12 | TMP 13 | COMPUTERNAME 14 | ALLUSERSPROFILE 15 | HOMEDRIVE 16 | HOMEPATH 17 | APPDATA 18 | USERDOMAIN 19 | LOCALAPPDATA 20 | USERNAME 21 | USERPROFILE 22 | NUMBER_OF_PROCESSORS 23 | PROCESSOR_ARCHITECTURE 24 | PROCESSOR_IDENTIFIER 25 | PROCESSOR_LEVEL 26 | PROCESSOR_REVISION 27 | OS 28 | PATHEXT 29 | -------------------------------------------------------------------------------- /scripts/go_test_no_env: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | ENV="" 6 | EOL=$'\n' 7 | while read -r name; do 8 | if [[ -n "$name" ]]; then 9 | ENV+="$name=${!name}${EOL}" 10 | fi 11 | done <<<"$(cat ./scripts/envs/allowlist_common.env ./scripts/envs/allowlist_unix.env)" 12 | 13 | # shellcheck disable=SC2046 14 | # disable word splitting warning as we actually need it here 15 | env -i $ENV go test "$@" 16 | -------------------------------------------------------------------------------- /scripts/go_test_no_env.ps1: -------------------------------------------------------------------------------- 1 | foreach ($var in get-childitem env:*) { 2 | [bool] $found = $false 3 | foreach ($name in get-content ./scripts/envs/allowlist_common.env, ./scripts/envs/allowlist_windows.env) { 4 | if ($var.name -eq $name) { 5 | $found = $true 6 | break 7 | } 8 | } 9 | 10 | if (!$found) { 11 | remove-item "env:$($var.name)" 12 | } 13 | } 14 | 15 | go test $args 16 | -------------------------------------------------------------------------------- /scripts/vagrant/provision/base.ps1: -------------------------------------------------------------------------------- 1 | $goVersion = "1.17.7" 2 | $gitVersion = "2.23.0" 3 | $powerShellCoreVersion = "7.1.1" 4 | $srcFolder = "C:\GitLab-Runner" 5 | 6 | [environment]::SetEnvironmentVariable("RUNNER_SRC", $srcFolder, "Machine") 7 | 8 | Write-Host "Installing Chocolatey" 9 | Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) 10 | 11 | Write-Host "Installing Go" 12 | choco install golang -y --version $goVersion 13 | 14 | Write-Host "Installing Git" 15 | choco install git -y --version $gitVersion 16 | 17 | Write-Host "Install PowerShell Core" 18 | choco install powershell-core -y --version $powerShellCoreVersion 19 | -------------------------------------------------------------------------------- /scripts/vagrant/provision/enable_developer_mode.ps1: -------------------------------------------------------------------------------- 1 | Write-Output "Enable Developer Mode" 2 | 3 | # Create AppModelUnlock if it doesn't exist, required for enabling Developer Mode 4 | $RegistryKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" 5 | if (-not(Test-Path -Path $RegistryKeyPath)) { 6 | New-Item -Path $RegistryKeyPath -ItemType Directory -Force 7 | } 8 | 9 | # Add registry value to enable Developer Mode 10 | New-ItemProperty -Path $RegistryKeyPath -Name AllowDevelopmentWithoutDevLicense -PropertyType DWORD -Value 1 11 | -------------------------------------------------------------------------------- /scripts/vagrant/provision/enable_sshd.ps1: -------------------------------------------------------------------------------- 1 | # Taken from https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse 2 | # We need to make sure the latest updates are installed as mentioned in https://github.com/MicrosoftDocs/windowsserverdocs/issues/2074 3 | 4 | Write-Output "Enabling OpenSSH" 5 | 6 | Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 7 | 8 | # Set services to start automatically on boot. 9 | Set-Service sshd -StartupType Automatic 10 | 11 | # Start the services for the first time. 12 | Start-Service sshd 13 | -------------------------------------------------------------------------------- /scripts/vagrant/provision/install_PSWindowsUpdate.ps1: -------------------------------------------------------------------------------- 1 | # Install https://www.powershellgallery.com/packages/PSWindowsUpdate so tha we 2 | # can manually download windows update. 3 | Write-Output "Installing PSWindowsUpdate module" 4 | 5 | # Make sure we can download from the Powershell Gallery https://www.powershellgallery.com/ 6 | Install-PackageProvider -Name NuGet -Force 7 | Set-PSRepository -Name PSGallery -InstallationPolicy Trusted 8 | 9 | # Install the actual module. 10 | Install-Module -Name PSWindowsUpdate -Force 11 | -------------------------------------------------------------------------------- /scripts/vagrant/provision/windows_update.ps1: -------------------------------------------------------------------------------- 1 | # Please make sure https://www.powershellgallery.com/packages/PSWindowsUpdate/2.1.0.1 is installed. 2 | if (Get-Command -Module PSWindowsUpdate -errorAction SilentlyContinue) 3 | { 4 | Write-Output "Running windows update" 5 | Install-WindowsUpdate -AcceptAll -IgnoreReboot 6 | } 7 | else 8 | { 9 | Write-Error "PSWindowsUpdate is not installed, please check https://www.powershellgallery.com/packages/PSWindowsUpdate/" 10 | } 11 | -------------------------------------------------------------------------------- /session/proxy/mock_Pooler.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package proxy 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockPooler is an autogenerated mock type for the Pooler type 8 | type MockPooler struct { 9 | mock.Mock 10 | } 11 | 12 | // Pool provides a mock function with given fields: 13 | func (_m *MockPooler) Pool() Pool { 14 | ret := _m.Called() 15 | 16 | var r0 Pool 17 | if rf, ok := ret.Get(0).(func() Pool); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(Pool) 22 | } 23 | } 24 | 25 | return r0 26 | } 27 | -------------------------------------------------------------------------------- /session/proxy/mock_Requester.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package proxy 4 | 5 | import ( 6 | http "net/http" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockRequester is an autogenerated mock type for the Requester type 12 | type MockRequester struct { 13 | mock.Mock 14 | } 15 | 16 | // ProxyRequest provides a mock function with given fields: w, r, requestedURI, port, settings 17 | func (_m *MockRequester) ProxyRequest(w http.ResponseWriter, r *http.Request, requestedURI string, port string, settings *Settings) { 18 | _m.Called(w, r, requestedURI, port, settings) 19 | } 20 | -------------------------------------------------------------------------------- /session/terminal/mock_Conn.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package terminal 4 | 5 | import ( 6 | http "net/http" 7 | 8 | mock "github.com/stretchr/testify/mock" 9 | ) 10 | 11 | // MockConn is an autogenerated mock type for the Conn type 12 | type MockConn struct { 13 | mock.Mock 14 | } 15 | 16 | // Close provides a mock function with given fields: 17 | func (_m *MockConn) Close() error { 18 | ret := _m.Called() 19 | 20 | var r0 error 21 | if rf, ok := ret.Get(0).(func() error); ok { 22 | r0 = rf() 23 | } else { 24 | r0 = ret.Error(0) 25 | } 26 | 27 | return r0 28 | } 29 | 30 | // Start provides a mock function with given fields: w, r, timeoutCh, disconnectCh 31 | func (_m *MockConn) Start(w http.ResponseWriter, r *http.Request, timeoutCh chan error, disconnectCh chan error) { 32 | _m.Called(w, r, timeoutCh, disconnectCh) 33 | } 34 | -------------------------------------------------------------------------------- /session/terminal/mock_InteractiveTerminal.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.1.0. DO NOT EDIT. 2 | 3 | package terminal 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // MockInteractiveTerminal is an autogenerated mock type for the InteractiveTerminal type 8 | type MockInteractiveTerminal struct { 9 | mock.Mock 10 | } 11 | 12 | // Connect provides a mock function with given fields: 13 | func (_m *MockInteractiveTerminal) Connect() (Conn, error) { 14 | ret := _m.Called() 15 | 16 | var r0 Conn 17 | if rf, ok := ret.Get(0).(func() Conn); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).(Conn) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func() error); ok { 27 | r1 = rf() 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | -------------------------------------------------------------------------------- /session/terminal/terminal.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | ) 7 | 8 | type InteractiveTerminal interface { 9 | Connect() (Conn, error) 10 | } 11 | 12 | type Conn interface { 13 | Start(w http.ResponseWriter, r *http.Request, timeoutCh, disconnectCh chan error) 14 | Close() error 15 | } 16 | 17 | func ProxyTerminal(timeoutCh, disconnectCh, proxyStopCh chan error, proxyFunc func()) { 18 | disconnected := make(chan bool, 1) 19 | // terminal exit handler 20 | go func() { 21 | // wait for either session timeout or disconnection from the client 22 | select { 23 | case err := <-timeoutCh: 24 | proxyStopCh <- err 25 | case <-disconnected: 26 | // forward the disconnection event if there is any waiting receiver 27 | nonBlockingSend( 28 | disconnectCh, 29 | errors.New("finished proxying (client disconnected?)"), 30 | ) 31 | } 32 | }() 33 | 34 | proxyFunc() 35 | disconnected <- true 36 | } 37 | 38 | func nonBlockingSend(ch chan error, err error) { 39 | select { 40 | case ch <- err: 41 | default: 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /shells/bash_trap.go: -------------------------------------------------------------------------------- 1 | package shells 2 | 3 | import ( 4 | "gitlab.com/gitlab-org/gitlab-runner/common" 5 | "gitlab.com/gitlab-org/gitlab-runner/helpers/featureflags" 6 | ) 7 | 8 | type BashTrapShell struct { 9 | *BashShell 10 | } 11 | 12 | func (b *BashTrapShell) GenerateScript(buildStage common.BuildStage, info common.ShellScriptInfo) (string, error) { 13 | w := &BashWriter{ 14 | TemporaryPath: info.Build.TmpProjectDir(), 15 | Shell: b.Shell, 16 | checkForErrors: info.Build.IsFeatureFlagOn(featureflags.EnableBashExitCodeCheck), 17 | useNewEval: info.Build.IsFeatureFlagOn(featureflags.UseNewEvalStrategy), 18 | useNewEscape: info.Build.IsFeatureFlagOn(featureflags.UseNewShellEscape), 19 | usePosixEscape: info.Build.IsFeatureFlagOn(featureflags.PosixlyCorrectEscapes), 20 | useJSONTermination: true, 21 | } 22 | 23 | return b.generateScript(w, buildStage, info) 24 | } 25 | -------------------------------------------------------------------------------- /shells/consts.go: -------------------------------------------------------------------------------- 1 | package shells 2 | 3 | const ( 4 | OSWindows = "windows" 5 | OSLinux = "linux" 6 | ) 7 | -------------------------------------------------------------------------------- /shells/shell_writer.go: -------------------------------------------------------------------------------- 1 | package shells 2 | 3 | import ( 4 | "gitlab.com/gitlab-org/gitlab-runner/common" 5 | ) 6 | 7 | type ShellWriter interface { 8 | EnvVariableKey(name string) string 9 | Variable(variable common.JobVariable) 10 | Command(command string, arguments ...string) 11 | Line(text string) 12 | CheckForErrors() 13 | 14 | IfDirectory(path string) 15 | IfFile(file string) 16 | IfCmd(cmd string, arguments ...string) 17 | IfCmdWithOutput(cmd string, arguments ...string) 18 | Else() 19 | EndIf() 20 | 21 | Cd(path string) 22 | MkDir(path string) 23 | RmDir(path string) 24 | RmFile(path string) 25 | RmFilesRecursive(path string, name string) 26 | Absolute(path string) string 27 | Join(elem ...string) string 28 | TmpFile(name string) string 29 | 30 | MkTmpDir(name string) string 31 | 32 | Printf(fmt string, arguments ...interface{}) 33 | Noticef(fmt string, arguments ...interface{}) 34 | Warningf(fmt string, arguments ...interface{}) 35 | Errorf(fmt string, arguments ...interface{}) 36 | EmptyLine() 37 | 38 | SectionStart(id, command string) 39 | SectionEnd(id string) 40 | 41 | Finish(trace bool) string 42 | } 43 | -------------------------------------------------------------------------------- /shells/shell_writer_test.go: -------------------------------------------------------------------------------- 1 | //go:build !integration 2 | // +build !integration 3 | 4 | package shells_test 5 | 6 | import ( 7 | "strings" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "gitlab.com/gitlab-org/gitlab-runner/shells" 12 | "gitlab.com/gitlab-org/gitlab-runner/shells/shellstest" 13 | ) 14 | 15 | func TestShellWriterFlush(t *testing.T) { 16 | // scripts are typically copied over stdin, so without a terminating newline 17 | // flush, it'd be like writing a command to a terminal and never hitting 18 | // return. 19 | 20 | shellstest.OnEachShellWithWriter(t, func(t *testing.T, shell string, writer shells.ShellWriter) { 21 | assert.True(t, strings.HasSuffix(writer.Finish(false), "\n"), "shell writer should terminate with newline") 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /tests/dockerfiles/alpine-entrypoint/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | run apk --no-cache add su-exec 4 | 5 | COPY tests/dockerfiles/alpine-entrypoint/entrypoint.sh /entrypoint 6 | 7 | ENTRYPOINT ["/entrypoint"] 8 | -------------------------------------------------------------------------------- /tests/dockerfiles/alpine-entrypoint/Dockerfile.stderr: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | COPY tests/dockerfiles/alpine-entrypoint/entrypoint-stderr.sh /entrypoint 4 | 5 | # non-root user required to enable the docker executor's file ownership change 6 | USER 1000:1000 7 | 8 | ENTRYPOINT ["/entrypoint"] 9 | -------------------------------------------------------------------------------- /tests/dockerfiles/alpine-entrypoint/entrypoint-stderr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | echo "entrypoint stdout message" >&2 4 | 5 | exec "$@" -------------------------------------------------------------------------------- /tests/dockerfiles/alpine-entrypoint/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "this has been executed through a custom entrypoint" >> /tmp/debug.log 4 | 5 | su-exec nobody /bin/sh 6 | -------------------------------------------------------------------------------- /tests/dockerfiles/alpine-id-overflow/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14 2 | 3 | RUN rm /usr/bin/id && \ 4 | printf "#!/bin/sh -e\ncat /dev/zero\n" > /bin/id && \ 5 | chmod +x /bin/id 6 | 7 | # non-root user required to enable the docker executor's file ownership change 8 | USER 1000:1000 9 | 10 | ENTRYPOINT ["/bin/sh", "-c"] 11 | -------------------------------------------------------------------------------- /tests/dockerfiles/alpine-no-root/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.14.2 2 | 3 | RUN adduser alpine -D 4 | USER alpine 5 | -------------------------------------------------------------------------------- /tests/test_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | USER="$1" 5 | 6 | status() { 7 | pidof gitlab-runner 8 | } 9 | 10 | echo Checking existence of $USER... 11 | id -u "$USER" 12 | 13 | echo Check if /etc/gitlab-runner/config.toml is created... 14 | if [[ -f /etc/gitlab-runner/config.toml ]]; then 15 | CONFIG=$(ls -al /etc/gitlab-runner | grep config.toml) 16 | echo $CONFIG | grep "\-rw-------" 17 | echo $CONFIG | grep "root root" 18 | fi 19 | 20 | echo List of processes: 21 | ps auxf 22 | echo 23 | 24 | echo Checking if runner is running... 25 | status 26 | echo 27 | 28 | echo Testing help... 29 | gitlab-runner --help > /dev/null 30 | echo 31 | 32 | echo Stopping runner... 33 | gitlab-runner stop 34 | ! status 35 | echo 36 | 37 | echo Starting runner... 38 | gitlab-runner start 39 | sleep 1s 40 | status 41 | echo 42 | 43 | #echo Registering runner... 44 | #gitlab-runner register -n -u https://ci.gitlab.com/ -r 51cc1fe15c0ac7d3d5564e24ada4d1 45 | #echo 46 | 47 | echo Checking su... 48 | echo id | su --shell /bin/bash --login "$USER" 49 | -------------------------------------------------------------------------------- /tests/ubuntu/Makefile: -------------------------------------------------------------------------------- 1 | deps: 2 | vagrant plugin install vagrant-parallels 3 | 4 | virtualbox: 5 | vagrant up --provider=virtualbox --provision virtualbox 6 | vagrant halt -f virtualbox 7 | 8 | parallels: 9 | vagrant up --provider=parallels --provision parallels 10 | vagrant halt -f parallels 11 | -------------------------------------------------------------------------------- /tests/ubuntu/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.define "virtualbox" do |vbconfig| 6 | vbconfig.vm.box = "ubuntu/trusty64" 7 | vbconfig.vm.provider "virtualbox" do |vb| 8 | vb.name = "ubuntu-runner" 9 | vb.gui = false 10 | vb.memory = "256" 11 | end 12 | end 13 | 14 | config.vm.define "parallels" do |prlconfig| 15 | prlconfig.vm.box = "parallels/ubuntu-14.04" 16 | prlconfig.vm.provider "parallels" do |prl| 17 | prl.name = "ubuntu-runner" 18 | prl.memory = 256 19 | prl.update_guest_tools = true 20 | end 21 | end 22 | 23 | config.vm.synced_folder '.', '/vagrant', disabled: true 24 | 25 | config.vm.provision "shell", inline: <<-SHELL 26 | sudo apt-get update -yqqq 27 | sudo apt-get install -yqqq git-core 28 | SHELL 29 | end 30 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package main 5 | 6 | // These imports are to force `go mod tidy` not to remove that tools we depend 7 | // on development. This is explained in great detail in 8 | // https://marcofranssen.nl/manage-go-tools-via-go-modules/ 9 | import ( 10 | _ "github.com/boumenot/gocover-cobertura" // code coverage format conversion tool for inline code coverage in MRs 11 | _ "github.com/dvyukov/go-fuzz/go-fuzz" // fuzz testing job 12 | _ "github.com/mitchellh/gox" // cross-compilation of the binary 13 | ) 14 | --------------------------------------------------------------------------------