├── .codecov.yaml ├── .git-hooks └── pre-push ├── .gitattributes ├── .github ├── actionlint-matcher.json ├── codeql │ └── codeql-config.yaml └── workflows │ ├── ci.yaml │ ├── codeql.yaml │ ├── download.yaml │ ├── generate.yaml │ ├── matcher.yaml │ └── release.yaml ├── .gitignore ├── .goreleaser.yaml ├── .pre-commit-hooks.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── HomebrewFormula └── actionlint.rb ├── LICENSE.txt ├── Makefile ├── README.md ├── action_metadata.go ├── action_metadata_test.go ├── all_webhooks.go ├── all_webhooks_test.go ├── ast.go ├── ast_test.go ├── availability.go ├── availability_test.go ├── cmd └── actionlint │ └── main.go ├── command.go ├── command_test.go ├── config.go ├── config_test.go ├── doc.go ├── docs ├── README.md ├── api.md ├── checks.md ├── config.md ├── install.md ├── reference.md └── usage.md ├── error.go ├── error_test.go ├── example_test.go ├── example_your_own_rule_test.go ├── expr.go ├── expr_ast.go ├── expr_insecure.go ├── expr_insecure_test.go ├── expr_lexer.go ├── expr_lexer_test.go ├── expr_parser.go ├── expr_parser_test.go ├── expr_sema.go ├── expr_sema_test.go ├── expr_test.go ├── expr_type.go ├── expr_type_test.go ├── fuzz ├── README.md ├── check.go ├── expr.go ├── glob.go └── parse.go ├── glob.go ├── glob_test.go ├── go.mod ├── go.sum ├── linter.go ├── linter_test.go ├── man └── actionlint.1.ronn ├── parse.go ├── pass.go ├── playground ├── .gitignore ├── .prettierrc.json ├── .stylelintrc.json ├── Makefile ├── README.md ├── deploy.bash ├── eslint.config.mjs ├── index.html ├── index.ts ├── lib.d.ts ├── main.go ├── package.json ├── post-install.bash ├── style.css ├── test.ts ├── tsconfig.eslint.json └── tsconfig.json ├── popular_actions.go ├── popular_actions_test.go ├── process.go ├── process_test.go ├── project.go ├── project_test.go ├── quotes.go ├── quotes_test.go ├── reusable_workflow.go ├── reusable_workflow_test.go ├── rule.go ├── rule_action.go ├── rule_credentials.go ├── rule_deprecated_commands.go ├── rule_deprecated_commands_test.go ├── rule_env_var.go ├── rule_env_var_test.go ├── rule_events.go ├── rule_expression.go ├── rule_glob.go ├── rule_id.go ├── rule_id_test.go ├── rule_if_cond.go ├── rule_if_cond_test.go ├── rule_job_needs.go ├── rule_matrix.go ├── rule_permissions.go ├── rule_pyflakes.go ├── rule_pyflakes_test.go ├── rule_runner_label.go ├── rule_runner_label_test.go ├── rule_shell_name.go ├── rule_shellcheck.go ├── rule_shellcheck_test.go ├── rule_test.go ├── rule_workflow_call.go ├── rule_workflow_call_test.go ├── scripts ├── bump-version.bash ├── check-checks │ ├── .gitignore │ ├── README.md │ ├── main.go │ ├── main_test.go │ └── testdata │ │ ├── err │ │ ├── duplicate_id.md │ │ ├── duplicate_title.md │ │ ├── empty.md │ │ ├── empty_anchor_id.md │ │ ├── empty_input.md │ │ ├── empty_input_with_skip_output.md │ │ ├── empty_input_with_skip_output_and_playground.md │ │ ├── empty_input_with_skip_playground.md │ │ ├── missing_output_and_playground_link.md │ │ ├── missing_output_block.md │ │ ├── missing_output_header.md │ │ ├── missing_playground_link.md │ │ ├── no_error.md │ │ ├── no_example.md │ │ ├── no_input.md │ │ ├── no_input_with_skip_output.md │ │ ├── no_input_with_skip_output_and_playground.md │ │ ├── no_input_with_skip_playground.md │ │ ├── no_output.md │ │ ├── no_playground_link.md │ │ ├── too_long_input_line.md │ │ ├── unclosed_example_input.md │ │ └── unclosed_output.md │ │ └── ok │ │ ├── minimal.in │ │ ├── minimal.out │ │ ├── multi_examples.in │ │ ├── multi_examples.out │ │ ├── multi_sections.in │ │ ├── multi_sections.out │ │ ├── replace_absolute_path.in │ │ ├── replace_absolute_path.out │ │ ├── skip_output.in │ │ ├── skip_output.out │ │ ├── skip_output_and_playground.in │ │ ├── skip_output_and_playground.out │ │ ├── skip_playground.in │ │ ├── skip_playground.out │ │ ├── strip_comment_playground_link.in │ │ ├── strip_comment_playground_link.out │ │ ├── unknown_anchors.in │ │ ├── unknown_anchors.out │ │ ├── unknown_code_blocks.in │ │ └── unknown_code_blocks.out ├── download-actionlint.bash ├── generate-actionlint-matcher │ ├── README.md │ ├── main.mjs │ ├── object.mjs │ ├── test.mjs │ └── test │ │ ├── escape.txt │ │ ├── no_escape.txt │ │ └── want.json ├── generate-availability │ ├── .gitignore │ ├── README.md │ ├── main.go │ ├── main_test.go │ └── testdata │ │ ├── broken_table.md │ │ ├── else_directive.md │ │ ├── no_heading.md │ │ ├── no_table.md │ │ ├── ok.go │ │ └── ok.md ├── generate-popular-actions │ ├── .gitignore │ ├── README.md │ ├── main.go │ ├── main_test.go │ ├── popular_actions.json │ └── testdata │ │ ├── go │ │ ├── fetched.go │ │ ├── no_new_version.go │ │ ├── outdated.go │ │ ├── skip_both.go │ │ ├── skip_inputs.go │ │ └── skip_outputs.go │ │ ├── jsonl │ │ ├── broken.jsonl │ │ ├── known_outdated.jsonl │ │ ├── no_new_version.jsonl │ │ ├── outdated.jsonl │ │ ├── skip_both.jsonl │ │ ├── skip_inputs.jsonl │ │ └── skip_outputs.jsonl │ │ └── registry │ │ ├── broken.json │ │ ├── empty_next_version.json │ │ ├── empty_slug.json │ │ ├── fetch.json │ │ ├── known_outdated.yaml │ │ ├── new_release.json │ │ ├── no_new_version.json │ │ ├── outdated.json │ │ ├── repo_not_found.json │ │ ├── skip_both.json │ │ ├── skip_inputs.json │ │ └── skip_outputs.json ├── generate-webhook-events │ ├── .gitignore │ ├── README.md │ ├── main.go │ ├── main_test.go │ └── testdata │ │ ├── no_heading.md │ │ ├── no_hook_name_link.md │ │ ├── no_hooks.md │ │ ├── ok.go │ │ └── ok.md ├── test-download-actionlint.bash └── yaml-to-playground-url.js ├── staticcheck.conf └── testdata ├── action_metadata ├── action-yaml │ └── action.yaml ├── action-yml │ └── action.yml ├── branding │ └── action.yml ├── broken │ └── action.yaml ├── composite │ └── action.yaml ├── docker │ ├── Dockerfile │ └── action.yaml ├── empty │ └── action.yml ├── input-duplicate │ └── action.yml ├── output-duplicate │ └── action.yml └── uppercase │ └── action.yml ├── bench ├── expressions.txt ├── many_scripts.yaml ├── minimal.yaml └── small.yaml ├── config ├── broken.yml ├── ok.yml └── projects │ ├── err │ └── .github │ │ ├── actionlint.yaml │ │ └── workflows │ │ └── test.yaml │ ├── none │ └── .github │ │ └── workflows │ │ └── test.yaml │ ├── ok │ └── .github │ │ ├── actionlint.yaml │ │ └── workflows │ │ └── test.yaml │ └── yml │ └── .github │ ├── actionlint.yml │ └── workflows │ └── test.yaml ├── err ├── .github │ └── workflows │ │ └── called-workflow.yml ├── assign_expression.out ├── assign_expression.yaml ├── case_sensitive_keys.out ├── case_sensitive_keys.yaml ├── context_availability.out ├── context_availability.yaml ├── cron_5minutes_limit.out ├── cron_5minutes_limit.yaml ├── deprecated_workflow_commands.out ├── deprecated_workflow_commands.yaml ├── duplicate_keys.out ├── duplicate_keys.yaml ├── empty.out ├── empty.yaml ├── empty_on.out ├── empty_on.yaml ├── empty_sequence_or_string.out ├── empty_sequence_or_string.yaml ├── env_context_banned.out ├── env_context_banned.yaml ├── evaluated_template.out ├── evaluated_template.yaml ├── exclusive_webhook_filters.out ├── exclusive_webhook_filters.yaml ├── expr_check_in_env_var_name.out ├── expr_check_in_env_var_name.yaml ├── expr_check_in_matrix_row_assign.out ├── expr_check_in_matrix_row_assign.yaml ├── expr_check_in_services.out ├── expr_check_in_services.yaml ├── expr_in_default_input.out ├── expr_in_default_input.yaml ├── github_script_untrusted_input.out ├── github_script_untrusted_input.yaml ├── glob_more.out ├── glob_more.yaml ├── if_cond_always_evaluated_to_true.out ├── if_cond_always_evaluated_to_true.yaml ├── inputs_without_workflow_call_event.out ├── inputs_without_workflow_call_event.yaml ├── invalid_comparisons.out ├── invalid_comparisons.yaml ├── invalid_event_filters.out ├── invalid_event_filters.yaml ├── invalid_float_at_timeout_minutes.out ├── invalid_float_at_timeout_minutes.yaml ├── invalid_id.out ├── invalid_id.yaml ├── invalid_int_at_max_parallel.out ├── invalid_int_at_max_parallel.yaml ├── invalid_json_in_fromjson.out ├── invalid_json_in_fromjson.yaml ├── invalid_runner_labels.out ├── invalid_runner_labels.yaml ├── invalid_steps.out ├── invalid_steps.yaml ├── issue102.out ├── issue102.yaml ├── issue151_child_of_child_job.out ├── issue151_child_of_child_job.yaml ├── issue155_env_in_job_level_if.out ├── issue155_env_in_job_level_if.yaml ├── issue170_empty_permissions.out ├── issue170_empty_permissions.yaml ├── issue193.out ├── issue193.yaml ├── issue207_work_dir_with_uses.out ├── issue207_work_dir_with_uses.yaml ├── issue280_runs_on.out ├── issue280_runs_on.yaml ├── macos12_runner.out ├── macos12_runner.yaml ├── macos_10.15_removed.out ├── macos_10.15_removed.yaml ├── matrix_exclude_mismatch.out ├── matrix_exclude_mismatch.yaml ├── matrix_exclude_no_match.out ├── matrix_exclude_no_match.yaml ├── minimal_cycle_in_needs.out ├── minimal_cycle_in_needs.yaml ├── missing_jobs.out ├── missing_jobs.yaml ├── missing_on.out ├── missing_on.yaml ├── missing_required_keys.out ├── missing_required_keys.yaml ├── nested_untrusted_input.out ├── nested_untrusted_input.yaml ├── object_at_runner_label.out ├── object_at_runner_label.yaml ├── one_error.out ├── one_error.yaml ├── outdated_actions.out ├── outdated_actions.yaml ├── outdated_popular_action.out ├── outdated_popular_action.yaml ├── outputs_map_object.out ├── outputs_map_object.yaml ├── outputs_of_action_skipping_inputs_check.out ├── outputs_of_action_skipping_inputs_check.yaml ├── permissions_upper_case.out ├── permissions_upper_case.yaml ├── pyflakes_job_default_shell.out ├── pyflakes_job_default_shell.yaml ├── pyflakes_step_shell.out ├── pyflakes_step_shell.yaml ├── pyflakes_workflow_default_shell.out ├── pyflakes_workflow_default_shell.yaml ├── random_order_cycle_in_needs.out ├── random_order_cycle_in_needs.yaml ├── reusable_workflow_empty_secrets.out ├── reusable_workflow_empty_secrets.yaml ├── run_name_check_expr.out ├── run_name_check_expr.yaml ├── runner_labels_conflict_matrix.out ├── runner_labels_conflict_matrix.yaml ├── shell_key_context_availability.out ├── shell_key_context_availability.yaml ├── shellcheck_default_shell_detection.out ├── shellcheck_default_shell_detection.yaml ├── special_function_availability.out ├── special_function_availability.yaml ├── unexpected_keys.out ├── unexpected_keys.yaml ├── upper_case_duplicate_keys.out ├── upper_case_duplicate_keys.yaml ├── variables_type_check.out ├── variables_type_check.yaml ├── workflow_call_event.out ├── workflow_call_event.yaml ├── workflow_call_inputs.out ├── workflow_call_inputs.yaml ├── workflow_call_invalid_secrets.out ├── workflow_call_invalid_secrets.yaml ├── workflow_call_job.out ├── workflow_call_job.yaml ├── workflow_call_outputs_sema.out ├── workflow_call_outputs_sema.yaml ├── workflow_call_outputs_syntax.out ├── workflow_call_outputs_syntax.yaml ├── workflow_call_secrets.out ├── workflow_call_secrets.yaml ├── workflow_dispatch_input_types.out ├── workflow_dispatch_input_types.yaml ├── workflow_dispatch_input_types_2.out ├── workflow_dispatch_input_types_2.yaml ├── workflow_dispatch_more_than_10_inputs.out ├── workflow_dispatch_more_than_10_inputs.yaml ├── workflow_dispatch_type_check_inputs.out └── workflow_dispatch_type_check_inputs.yaml ├── examples ├── .github │ └── actions │ │ ├── my-action-with-output │ │ ├── action.yaml │ │ └── index.js │ │ ├── my-action │ │ ├── action.yaml │ │ └── index.js │ │ └── my-invalid-action │ │ └── action.yml ├── action_metadata_syntax_validation.out ├── action_metadata_syntax_validation.yaml ├── broken_yaml.out ├── broken_yaml.yaml ├── builtin_func_special_checks.out ├── builtin_func_special_checks.yaml ├── comparison_strict_checks.out ├── comparison_strict_checks.yaml ├── contexts_and_builtin_funcs.out ├── contexts_and_builtin_funcs.yaml ├── contexts_special_functions_availability.out ├── contexts_special_functions_availability.yaml ├── contextual_matrix_values.out ├── contextual_matrix_values.yaml ├── contextual_needs_object.out ├── contextual_needs_object.yaml ├── contextual_steps_outputs.out ├── contextual_steps_outputs.yaml ├── cron_schedule_check.out ├── cron_schedule_check.yaml ├── cyclic_deps_needs.out ├── cyclic_deps_needs.yaml ├── deprecated_workflow_commands.out ├── deprecated_workflow_commands.yaml ├── detect_outdated_popular_actions.out ├── detect_outdated_popular_actions.yaml ├── env_var_names.out ├── env_var_names.yaml ├── expand_object.out ├── expand_object.yaml ├── expression_syntax_error.out ├── expression_syntax_error.yaml ├── glob.out ├── glob.yaml ├── hardcoded_credentials.out ├── hardcoded_credentials.yaml ├── id_naming_convention.out ├── id_naming_convention.yaml ├── if_cond_always_true.out ├── if_cond_always_true.yaml ├── invalid_action_format.out ├── invalid_action_format.yaml ├── invalid_ids_in_needs.out ├── invalid_ids_in_needs.yaml ├── job_step_ids_duplicate.out ├── job_step_ids_duplicate.yaml ├── local_action_inputs.out ├── local_action_inputs.yaml ├── local_action_outputs.out ├── local_action_outputs.yaml ├── main.out ├── main.yaml ├── matrix_checks.out ├── matrix_checks.yaml ├── missing_required_keys.out ├── missing_required_keys.yaml ├── not_persistent_matrix_values.out ├── not_persistent_matrix_values.yaml ├── permissions.out ├── permissions.yaml ├── popular_action_inputs.out ├── popular_action_inputs.yaml ├── popular_action_outputs.out ├── popular_action_outputs.yaml ├── pyflakes_integration.out ├── pyflakes_integration.yaml ├── reusable_workflow_outputs.out ├── reusable_workflow_outputs.yaml ├── runner_label_check.out ├── runner_label_check.yaml ├── runner_label_conflict.out ├── runner_label_conflict.yaml ├── shell_name_validation.out ├── shell_name_validation.yaml ├── shellcheck_integration.out ├── shellcheck_integration.yaml ├── type_checks.out ├── type_checks.yaml ├── unexpected_keys.out ├── unexpected_keys.yaml ├── unexpected_mapping_values.out ├── unexpected_mapping_values.yaml ├── untrusted_input.out ├── untrusted_input.yaml ├── webhook_checks.out ├── webhook_checks.yaml ├── workflow_call_definitions.out ├── workflow_call_definitions.yaml ├── workflow_call_jobs.out ├── workflow_call_jobs.yaml ├── workflow_dispatch_input_types.out ├── workflow_dispatch_input_types.yaml ├── workflow_inputs_secrets_types.out └── workflow_inputs_secrets_types.yaml ├── find_project ├── .github │ ├── reusable │ │ └── broken.yaml │ └── workflows │ │ └── test.yaml └── README.md ├── format ├── README.md ├── sarif_template.txt ├── test.json ├── test.jsonl ├── test.md ├── test.sarif └── test.yaml ├── ok ├── allow_any_outputs.yaml ├── bool_conversion.yaml ├── dynamic_shell_name.yaml ├── filters_for_specific_events.yaml ├── filters_ignore_for_specific_events.yaml ├── issue-101.yaml ├── issue-104.yaml ├── issue-113.yaml ├── issue-145.yaml ├── issue-151-child-of-child-job.yaml ├── issue-152.yaml ├── issue-164.yaml ├── issue-174-secret-description-is-optional.yaml ├── issue-205-closing-braces-in-string.yaml ├── issue-223.yaml ├── issue-235-expr-in-input-default-value.yaml ├── issue-249_matrix_exclude_filter_object_subset.yaml ├── issue-261_matrix_row_item_constructed_with_expr.yaml ├── issue-263-inputs-shared-by-multiple-events.yaml ├── issue-280_runs_on.yaml ├── issue-285_expr_assign_at_matrix_row.yaml ├── issue-294_matrix_row_array_expression.yaml ├── issue-30.yaml ├── issue-31.yaml ├── issue-312_expression_in_env_var.yaml ├── issue-355-shellcheck-sc2043.yaml ├── issue-384_narrow-logical-op.yaml ├── issue-402-expression-in-services.yaml ├── issue-409-shellcheck-default-shell.yaml ├── issue-412_runner_environment_prop.yaml ├── issue-414_expression_in_matrix_exclude.yaml ├── issue-442-download-artifact-v3-workaround.yaml ├── issue-45.yaml ├── issue-495_with_args.yaml ├── issue-53-shellcheck-sc2154.yaml ├── issue-66.yaml ├── issue-67.yaml ├── issue-87.yaml ├── matrix_exclude_match_to_expr.yaml ├── matrix_expression_in_exclude.yaml ├── matrix_in_array_at_runs_on.yaml ├── minimal.yaml ├── multi_labels_no_conflict.yaml ├── nested_workflow_call.yaml ├── no_description_workflow_call.yaml ├── no_local_action_found.yaml ├── operator_outside_expr.yaml ├── outputs_in_environment_url.yaml ├── pyflakes_python_shell.yaml ├── reusable_workflow_inherit_secrets.yaml ├── run_name.yaml ├── shell_name_case_insensitive.yaml ├── shellcheck_detect_shell_from_runner.yaml ├── shellcheck_job_default_shell.yaml ├── shellcheck_step_shell.yaml ├── shellcheck_workflow_default_shell.yaml ├── skip_inputs.yaml ├── special_function_availability.yaml ├── untrusted_inputs.yaml ├── using_reusable_workflow_call_outputs.yaml ├── valid_comparisons.yaml ├── variables.yaml ├── workflow_call_event.yaml ├── workflow_call_job.yaml ├── workflow_call_outputs.yaml ├── workflow_call_outputs_sema.yaml ├── workflow_call_with_strategy.yaml ├── workflow_dispatch_input_types.yaml └── workflow_dispatch_upper_case_inputs.yaml ├── projects ├── README.md ├── broken_local_action.out ├── broken_local_action │ ├── action │ │ ├── broken_input │ │ │ └── action.yml │ │ ├── broken_output │ │ │ └── action.yml │ │ ├── duplicate_input │ │ │ └── action.yml │ │ └── duplicate_output │ │ │ └── action.yml │ └── workflows │ │ └── test.yaml ├── broken_reusable_workflow.out ├── broken_reusable_workflow │ ├── README.md │ ├── reusable │ │ ├── broken.yaml │ │ ├── broken_input.yaml │ │ ├── broken_secrets.yaml │ │ ├── no_hook.yaml │ │ └── no_on.yaml │ └── workflows │ │ └── test.yaml ├── config_variables.out ├── config_variables │ ├── actionlint.yaml │ └── workflows │ │ └── test.yaml ├── example_inputs_secrets_in_workflow_call.out ├── example_inputs_secrets_in_workflow_call │ ├── .github │ │ └── workflows │ │ │ └── reusable.yaml │ └── workflows │ │ └── test.yaml ├── example_workflow_call_outputs_downstream_jobs.out ├── example_workflow_call_outputs_downstream_jobs │ ├── .github │ │ └── workflows │ │ │ └── get-build-info.yaml │ └── workflows │ │ └── test.yaml ├── issue-136.out ├── issue-136 │ └── workflows │ │ ├── reusable.yaml │ │ └── test.yaml ├── issue173.out ├── issue173 │ ├── action │ │ ├── action.yaml │ │ └── index.js │ └── workflows │ │ ├── workflow1.yaml │ │ └── workflow2.yaml ├── local_action_branding.out ├── local_action_branding │ ├── action │ │ ├── action.yaml │ │ └── index.js │ └── workflows │ │ └── test.yaml ├── local_action_case_insensitive.out ├── local_action_case_insensitive │ ├── action │ │ ├── action.yaml │ │ └── index.js │ └── workflows │ │ └── test.yaml ├── local_action_empty.out ├── local_action_empty │ ├── action │ │ ├── action.yaml │ │ └── index.js │ └── workflows │ │ └── test.yaml ├── local_action_invalid.out ├── local_action_invalid │ ├── branding │ │ ├── action.yaml │ │ └── index.js │ ├── no_desc │ │ ├── action.yaml │ │ └── index.js │ ├── no_name │ │ ├── action.yaml │ │ └── index.js │ └── workflows │ │ └── test.yaml ├── local_action_invalid_runners.out ├── local_action_invalid_runners │ ├── invalid_node_version │ │ ├── action.yaml │ │ └── index.js │ ├── missing_runs │ │ └── action.yaml │ ├── missing_using │ │ └── action.yaml │ ├── node12 │ │ ├── action.yaml │ │ └── index.js │ ├── old_node │ │ ├── action.yaml │ │ └── index.js │ ├── unknown_runner │ │ └── action.yaml │ └── workflows │ │ └── test.yaml ├── local_composite_action.out ├── local_composite_action │ ├── all_invalid_keys │ │ └── action.yaml │ ├── missing_steps │ │ └── action.yaml │ ├── ok │ │ └── action.yaml │ └── workflows │ │ └── test.yaml ├── local_docker_action.out ├── local_docker_action │ ├── all_invalid_keys │ │ └── action.yaml │ ├── invalid_dockerfile │ │ ├── Dockerfile2 │ │ └── action.yaml │ ├── missing_files │ │ └── action.yaml │ ├── missing_image │ │ └── action.yaml │ ├── ok_all_keys │ │ ├── Dockerfile │ │ ├── action.yaml │ │ ├── main.sh │ │ ├── post.sh │ │ └── pre.sh │ ├── ok_docker_docker.io │ │ └── action.yaml │ ├── ok_docker_ghcr.io │ │ └── action.yaml │ ├── ok_dockerfile │ │ ├── Dockerfile │ │ └── action.yaml │ ├── ok_dockerfile_subdir │ │ ├── action.yaml │ │ └── subdir │ │ │ └── Dockerfile │ ├── ok_registry │ │ └── action.yaml │ └── workflows │ │ └── test.yaml ├── local_javascript_action.out ├── local_javascript_action │ ├── all_invalid_keys │ │ ├── action.yaml │ │ └── index.js │ ├── invalid_if_sections │ │ ├── action.yaml │ │ └── index.js │ ├── missing_files │ │ └── action.yaml │ ├── missing_main │ │ └── action.yaml │ ├── ok │ │ ├── action.yaml │ │ └── index.js │ ├── ok_all_optional │ │ ├── action.yaml │ │ ├── index.js │ │ ├── post.js │ │ └── pre.js │ └── workflows │ │ └── test.yaml ├── paths_config.out ├── paths_config │ ├── actionlint.yaml │ └── workflows │ │ ├── bar.yaml │ │ ├── foo.yaml │ │ └── nested │ │ └── piyo.yaml ├── recursive_workflow_call.out ├── recursive_workflow_call │ └── workflows │ │ └── recursive.yaml ├── user_defined_runner_label.out ├── user_defined_runner_label │ ├── actionlint.yaml │ └── workflows │ │ └── test.yaml ├── workflow_call_inherit_secrets.out ├── workflow_call_inherit_secrets │ └── workflows │ │ ├── reusable.yaml │ │ └── test.yaml ├── workflow_call_input_type_check.out ├── workflow_call_input_type_check │ └── workflows │ │ ├── reusable.yaml │ │ └── test.yaml ├── workflow_call_missing_required.out ├── workflow_call_missing_required │ └── workflows │ │ ├── reusable.yaml │ │ └── test.yaml ├── workflow_call_not_found.out ├── workflow_call_not_found │ └── workflows │ │ └── test.yaml ├── workflow_call_ok.out ├── workflow_call_ok │ └── workflows │ │ ├── empty1.yaml │ │ ├── empty2.yaml │ │ ├── empty3.yaml │ │ ├── reusable_all_optional.yaml │ │ ├── reusable_all_required.yaml │ │ └── test.yaml ├── workflow_call_undefined.out ├── workflow_call_undefined │ └── workflows │ │ ├── empty_reusable.yaml │ │ ├── reusable.yaml │ │ └── test.yaml ├── workflow_call_upper_case.out └── workflow_call_upper_case │ ├── reusable │ ├── lower.yaml │ └── upper.yaml │ └── workflows │ ├── missing.yaml │ ├── ok_lower.yaml │ ├── ok_upper.yaml │ ├── output.yaml │ └── undefined.yaml ├── realworld ├── .gitignore └── dataset.zip └── reusable_workflow_metadata ├── broken.yaml ├── broken_inputs.yaml ├── broken_secrets.yaml ├── no_hook.yaml ├── no_on.yaml └── ok.yaml /.codecov.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.codecov.com/docs/commit-status#disabling-a-status 2 | coverage: 3 | status: 4 | project: off 5 | patch: off 6 | 7 | comment: false 8 | -------------------------------------------------------------------------------- /.git-hooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -o pipefail 4 | 5 | if [[ "$CI" != "" ]]; then 6 | exit 7 | fi 8 | 9 | set -x 10 | 11 | make build SKIP_GO_GENERATE=true 12 | make test SKIP_GO_GENERATE=true 13 | make lint SKIP_GO_GENERATE=true 14 | 15 | if [ -x ./actionlint ]; then 16 | ./actionlint 17 | fi 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /testdata/** -text 2 | /scripts/generate-popular-actions/testdata/** -text 3 | /scripts/generate-webhook-events/testdata/** -text 4 | /scripts/generate-availability/testdata/** -text 5 | /scripts/generate-actionlint-matcher/test/** -text 6 | -------------------------------------------------------------------------------- /.github/actionlint-matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "actionlint", 5 | "pattern": [ 6 | { 7 | "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "message": 4, 12 | "code": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yaml: -------------------------------------------------------------------------------- 1 | paths-ignore: 2 | - testdata 3 | -------------------------------------------------------------------------------- /.github/workflows/download.yaml: -------------------------------------------------------------------------------- 1 | name: Download script 2 | on: 3 | push: 4 | paths: 5 | - scripts/download-actionlint.bash 6 | - scripts/test-download-actionlint.bash 7 | pull_request: 8 | paths: 9 | - scripts/download-actionlint.bash 10 | - scripts/test-download-actionlint.bash 11 | workflow_dispatch: 12 | 13 | jobs: 14 | download: 15 | name: Test download script 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, macos-latest, windows-latest] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v4 22 | - run: ./scripts/test-download-actionlint.bash 23 | shell: bash 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /actionlint 2 | /actionlint.exe 3 | /.testtimestamp 4 | /.linttimestamp 5 | /.bumptimestamp 6 | /env.sh 7 | /.github/actionlint.yaml 8 | /.github/actionlint.yml 9 | /actionlint_fuzz-fuzz.zip 10 | /corpus 11 | /crashers 12 | /man/actionlint.1 13 | /man/actionlint.1.html 14 | /playground-dist 15 | /actionlint-workflow-ast 16 | /.git-hooks/.timestamp 17 | /.idea 18 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GOLANG_VER=latest 2 | ARG ALPINE_VER=latest 3 | 4 | FROM golang:${GOLANG_VER} as builder 5 | WORKDIR /go/src/app 6 | COPY go.* *.go ./ 7 | COPY cmd cmd/ 8 | ENV CGO_ENABLED 0 9 | ARG ACTIONLINT_VER= 10 | RUN go build -v -ldflags "-s -w -X github.com/rhysd/actionlint.version=${ACTIONLINT_VER}" ./cmd/actionlint 11 | 12 | FROM koalaman/shellcheck-alpine:stable as shellcheck 13 | 14 | FROM alpine:${ALPINE_VER} 15 | COPY --from=builder /go/src/app/actionlint /usr/local/bin/ 16 | COPY --from=shellcheck /bin/shellcheck /usr/local/bin/shellcheck 17 | RUN apk add --no-cache py3-pyflakes 18 | USER guest 19 | ENTRYPOINT ["/usr/local/bin/actionlint"] 20 | -------------------------------------------------------------------------------- /all_webhooks_test.go: -------------------------------------------------------------------------------- 1 | package actionlint 2 | 3 | import "testing" 4 | 5 | func TestGeneratedAllWebhooks(t *testing.T) { 6 | if len(AllWebhookTypes) == 0 { 7 | t.Fatal("AllWebhookTypes is empty") 8 | } 9 | 10 | for name, types := range AllWebhookTypes { 11 | if name == "" { 12 | t.Errorf("Name is empty (types=%v)", types) 13 | continue 14 | } 15 | 16 | seen := map[string]struct{}{} 17 | for _, ty := range types { 18 | if _, ok := seen[ty]; ok { 19 | t.Errorf("type %q duplicates in webhook %q: %v", ty, name, types) 20 | } else { 21 | seen[ty] = struct{}{} 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cmd/actionlint/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/rhysd/actionlint" 7 | ) 8 | 9 | func main() { 10 | cmd := actionlint.Command{ 11 | Stdin: os.Stdin, 12 | Stdout: os.Stdout, 13 | Stderr: os.Stderr, 14 | } 15 | os.Exit(cmd.Main(os.Args)) 16 | } 17 | -------------------------------------------------------------------------------- /fuzz/README.md: -------------------------------------------------------------------------------- 1 | See [CONTRIBUTING.md](../CONTRIBUTING.md) about this directory. 2 | -------------------------------------------------------------------------------- /fuzz/expr.go: -------------------------------------------------------------------------------- 1 | //go:build gofuzz 2 | 3 | package actionlint_fuzz 4 | 5 | import ( 6 | "unicode/utf8" 7 | 8 | "github.com/rhysd/actionlint" 9 | ) 10 | 11 | func FuzzExprParse(data []byte) int { 12 | if !utf8.Valid(data) { 13 | return 0 14 | } 15 | 16 | l := actionlint.NewExprLexer(string(data)) 17 | p := actionlint.NewExprParser() 18 | e, err := p.Parse(l) 19 | if err != nil { 20 | return 0 21 | } 22 | 23 | c := actionlint.NewExprSemanticsChecker(true, nil) 24 | c.Check(e) 25 | 26 | return 1 27 | } 28 | -------------------------------------------------------------------------------- /fuzz/glob.go: -------------------------------------------------------------------------------- 1 | //go:build gofuzz 2 | 3 | package actionlint_fuzz 4 | 5 | import ( 6 | "github.com/rhysd/actionlint" 7 | ) 8 | 9 | func FuzzGlobGitRef(data []byte) int { 10 | errs := actionlint.ValidateRefGlob(string(data)) 11 | if len(errs) > 0 { 12 | return 0 13 | } 14 | return 1 15 | } 16 | 17 | func FuzzGlobFilePath(data []byte) int { 18 | errs := actionlint.ValidatePathGlob(string(data)) 19 | if len(errs) > 0 { 20 | return 0 21 | } 22 | return 1 23 | } 24 | -------------------------------------------------------------------------------- /fuzz/parse.go: -------------------------------------------------------------------------------- 1 | //go:build gofuzz 2 | 3 | package actionlint_fuzz 4 | 5 | import ( 6 | "github.com/rhysd/actionlint" 7 | "gopkg.in/yaml.v3" 8 | ) 9 | 10 | func canParseByGoYAML(data []byte) (ret bool) { 11 | ret = true 12 | defer func() { 13 | if err := recover(); err != nil { 14 | ret = false 15 | } 16 | }() 17 | var n yaml.Node 18 | yaml.Unmarshal(data, &n) 19 | return 20 | } 21 | 22 | func FuzzParse(data []byte) int { 23 | if !canParseByGoYAML(data) { 24 | return 0 25 | } 26 | 27 | if _, errs := actionlint.Parse(data); len(errs) > 0 { 28 | return 0 29 | } 30 | 31 | return 1 32 | } 33 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rhysd/actionlint 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/bmatcuk/doublestar/v4 v4.8.0 7 | github.com/fatih/color v1.18.0 8 | github.com/google/go-cmp v0.6.0 9 | github.com/mattn/go-colorable v0.1.14 10 | github.com/mattn/go-runewidth v0.0.16 11 | github.com/mattn/go-shellwords v1.0.12 12 | github.com/robfig/cron/v3 v3.0.1 13 | github.com/yuin/goldmark v1.7.8 14 | golang.org/x/sync v0.10.0 15 | golang.org/x/sys v0.29.0 16 | gopkg.in/yaml.v3 v3.0.1 17 | ) 18 | 19 | require ( 20 | github.com/mattn/go-isatty v0.0.20 // indirect 21 | github.com/rivo/uniseg v0.4.7 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /playground/.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | /main.wasm 3 | /node_modules 4 | /package-lock.json 5 | /image 6 | /*.js 7 | /*.js.map 8 | /.testtimestamp 9 | -------------------------------------------------------------------------------- /playground/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "semi": true, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "printWidth": 120, 7 | "arrowParens": "avoid", 8 | "overrides": [ 9 | { 10 | "files": "*.css", 11 | "options": { 12 | "tabWidth": 2, 13 | "printWidth": -1 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /playground/.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "selector-class-pattern": null 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /playground/lib.d.ts: -------------------------------------------------------------------------------- 1 | interface ActionlintError { 2 | kind: string; 3 | message: string; 4 | line: number; 5 | column: number; 6 | } 7 | 8 | interface Window { 9 | runActionlint?(src: string): void; 10 | getYamlSource(): string; 11 | showError(msg: string): void; 12 | onCheckCompleted(errs: ActionlintError[]): void; 13 | dismissLoading(): void; 14 | } 15 | 16 | declare class Go { 17 | importObject: Imports; 18 | run(mod: Instance): Promise; 19 | } 20 | 21 | declare const isMobile: IsMobile.isMobileResult; 22 | -------------------------------------------------------------------------------- /playground/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "checkJs": true, 5 | "noEmit": true, 6 | "module": "nodenext", 7 | "moduleResolution": "nodenext" 8 | }, 9 | "files": [ 10 | "eslint.config.mjs" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "none", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "removeComments": true, 8 | "strict": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noUncheckedIndexedAccess": true, 14 | "noImplicitOverride": true, 15 | "esModuleInterop": true, 16 | "skipLibCheck": true 17 | }, 18 | "files": [ 19 | "index.ts", 20 | "lib.d.ts", 21 | "test.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /scripts/check-checks/.gitignore: -------------------------------------------------------------------------------- 1 | /update-checks-doc 2 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/duplicate_id.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | ``` 9 | 10 | Output: 11 | 12 | ``` 13 | This block will be auto-generated 14 | ``` 15 | 16 | [Playground](URL_WILL_BE_GENERATED) 17 | 18 | This section causes no error. 19 | 20 | 21 | ## Hello 2 22 | 23 | Example input: 24 | 25 | ```yaml 26 | on: push 27 | ``` 28 | 29 | Output: 30 | 31 | ``` 32 | This block will be auto-generated 33 | ``` 34 | 35 | [Playground](URL_WILL_BE_GENERATED) 36 | 37 | This section causes an error since same `id` is used. 38 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/duplicate_title.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | ``` 9 | 10 | Output: 11 | 12 | ``` 13 | This block will be auto-generated 14 | ``` 15 | 16 | [Playground](URL_WILL_BE_GENERATED) 17 | 18 | This section causes no error. 19 | 20 | 21 | ## Hello 22 | 23 | Example input: 24 | 25 | ```yaml 26 | on: push 27 | ``` 28 | 29 | Output: 30 | 31 | ``` 32 | This block will be auto-generated 33 | ``` 34 | 35 | [Playground](URL_WILL_BE_GENERATED) 36 | 37 | This section causes an error since same section title is used. 38 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/empty.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/scripts/check-checks/testdata/err/empty.md -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/empty_anchor_id.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | ``` 14 | 15 | Output: 16 | 17 | ``` 18 | This section will be generated 19 | ``` 20 | 21 | [Playground](URL_WILL_BE_GENERATED) 22 | 23 | The `id` attribute of the `` is an empty string. 24 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/empty_input.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | ``` 8 | 9 | Output: 10 | 11 | ``` 12 | This section will be generated 13 | ``` 14 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/empty_input_with_skip_output.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | ``` 8 | 9 | Output: 10 | 11 | 12 | ``` 13 | This section will NOT be updated 14 | ``` 15 | 16 | [Playground](URL_WILL_BE_GENERATED) 17 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/empty_input_with_skip_output_and_playground.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | ``` 8 | 9 | Output: 10 | 11 | 12 | ``` 13 | This section will NOT be updated 14 | ``` 15 | 16 | 17 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/empty_input_with_skip_playground.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | ``` 8 | 9 | Output: 10 | 11 | ``` 12 | This section will be updated 13 | ``` 14 | 15 | 16 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/missing_output_and_playground_link.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | ``` 14 | 15 | Everything after the input block is missing in this example. 16 | 17 | 18 | ## Hello 2 19 | 20 | Example input: 21 | 22 | ```yaml 23 | on: push 24 | jobs: 25 | test: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - run: echo ${{ unknown }} 29 | ``` 30 | 31 | Output: 32 | 33 | ``` 34 | This section will be generated 35 | ``` 36 | 37 | [Playground](URL_WILL_BE_GENERATED) 38 | 39 | This is minimal test case. 40 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_error.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo hello 13 | ``` 14 | 15 | Output: 16 | 17 | ``` 18 | ``` 19 | 20 | [Playground](URL_WILL_BE_GENERATED) 21 | 22 | The example input does not cause any error. 23 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_example.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | This rule has no example... 5 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_input.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Output: 5 | 6 | ``` 7 | This section will be generated 8 | ``` 9 | 10 | [Playground](URL_WILL_BE_GENERATED) 11 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_input_with_skip_output.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Output: 5 | 6 | 7 | ``` 8 | This section will NOT be updated 9 | ``` 10 | 11 | [Playground](URL_WILL_BE_GENERATED) 12 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_input_with_skip_output_and_playground.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Output: 5 | 6 | 7 | ``` 8 | This section will NOT be updated 9 | ``` 10 | 11 | 12 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_input_with_skip_playground.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Output: 5 | 6 | ``` 7 | This section will be updated 8 | ``` 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_output.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | ``` 14 | 15 | [Playground](URL_WILL_BE_GENERATED) 16 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/no_playground_link.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | ``` 14 | 15 | Output: 16 | 17 | ``` 18 | test.yaml:6:23: undefined variable "unknown". available variables are "env", "github", "inputs", "job", "matrix", "needs", "runner", "secrets", "steps", "strategy", "vars" [expression] 19 | | 20 | 6 | - run: echo ${{ unknown }} 21 | | ^~~~~~~ 22 | ``` 23 | 24 | 25 | ## Hello2 26 | 27 | ... 28 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/unclosed_example_input.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/err/unclosed_output.md: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | ``` 14 | 15 | Output: 16 | 17 | ``` 18 | This section will be generated 19 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/ok/minimal.in: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ unknown }} 13 | ``` 14 | 15 | Output: 16 | 17 | ``` 18 | This section will be generated 19 | ``` 20 | 21 | [Playground](URL_WILL_BE_GENERATED) 22 | 23 | This is minimal test case. 24 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/ok/replace_absolute_path.in: -------------------------------------------------------------------------------- 1 | 2 | ## Some check 3 | 4 | Example input: 5 | 6 | ```yaml 7 | on: push 8 | jobs: 9 | error: 10 | # This error message contains an abosolute file path 11 | uses: ./.github/workflows/not-existing-workflow.yml 12 | ``` 13 | 14 | Output: 15 | 16 | ``` 17 | THIS BLOCK WILL BE GENERATED 18 | ``` 19 | 20 | [Playground](https://rhysd.github.io/actionlint/#HASH_PART_WILL_BE_GENERATED) 21 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/ok/strip_comment_playground_link.in: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | Example input: 5 | 6 | ```yaml 7 | # This line is not included 8 | on: push 9 | jobs: 10 | test: 11 | # This line is not included 12 | runs-on: ubuntu-latest 13 | steps: 14 | # This line is not included 15 | - run: echo ${{ unknown }} 16 | # This line is not included 17 | ``` 18 | 19 | Output: 20 | 21 | ``` 22 | This section will be generated 23 | ``` 24 | 25 | [Playground](URL_WILL_BE_GENERATED) 26 | 27 | This is minimal test case. 28 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/ok/unknown_anchors.in: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | 5 | Example input: 6 | 7 | 8 | ```yaml 9 | on: push 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - run: echo ${{ unknown }} 15 | ``` 16 | 17 | 18 | Output: 19 | 20 | 21 | ``` 22 | This section will be generated 23 | ``` 24 | 25 | 26 | [Playground](URL_WILL_BE_GENERATED) 27 | 28 | 29 | This is minimal test case. 30 | -------------------------------------------------------------------------------- /scripts/check-checks/testdata/ok/unknown_code_blocks.in: -------------------------------------------------------------------------------- 1 | 2 | ## Hello 3 | 4 | ``` 5 | code block 6 | ``` 7 | 8 | Example input: 9 | 10 | ``` 11 | code block 12 | ``` 13 | 14 | ```yaml 15 | on: push 16 | jobs: 17 | test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - run: echo ${{ unknown }} 21 | ``` 22 | 23 | ``` 24 | code block 25 | ``` 26 | 27 | Output: 28 | 29 | ``` 30 | This section will be generated 31 | ``` 32 | 33 | ``` 34 | code block 35 | ``` 36 | 37 | [Playground](URL_WILL_BE_GENERATED) 38 | 39 | ``` 40 | code block 41 | ``` 42 | 43 | This is description. 44 | 45 | ``` 46 | code block 47 | ``` 48 | -------------------------------------------------------------------------------- /scripts/generate-actionlint-matcher/main.mjs: -------------------------------------------------------------------------------- 1 | import { promises as fs } from 'node:fs'; 2 | import object from './object.mjs'; 3 | 4 | async function main(args) { 5 | const json = JSON.stringify(object, null, 2); 6 | if (args.length === 0) { 7 | console.log(json); 8 | } else { 9 | const path = args[0]; 10 | await fs.writeFile(args[0], json + '\n', 'utf8'); 11 | console.log(`Wrote to ${path}`); 12 | } 13 | } 14 | 15 | main(process.argv.slice(2)); 16 | -------------------------------------------------------------------------------- /scripts/generate-actionlint-matcher/test/escape.txt: -------------------------------------------------------------------------------- 1 | ./testdata/err/one_error.yaml:6:41: "github.event.head_commit.message" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details [expression] 2 |  | 3 | 6 |  - run: echo "Checking commit '${{ github.event.head_commit.message }}'" 4 |  |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | -------------------------------------------------------------------------------- /scripts/generate-actionlint-matcher/test/no_escape.txt: -------------------------------------------------------------------------------- 1 | ./testdata/err/one_error.yaml:6:41: "github.event.head_commit.message" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details [expression] 2 | | 3 | 6 | - run: echo "Checking commit '${{ github.event.head_commit.message }}'" 4 | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | -------------------------------------------------------------------------------- /scripts/generate-actionlint-matcher/test/want.json: -------------------------------------------------------------------------------- 1 | [{"message":"\"github.event.head_commit.message\" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details","filepath":"./testdata/err/one_error.yaml","line":6,"column":41,"kind":"expression","snippet":" - run: echo \"Checking commit '${{ github.event.head_commit.message }}'\"\n ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~","end_column":72}] 2 | -------------------------------------------------------------------------------- /scripts/generate-availability/.gitignore: -------------------------------------------------------------------------------- 1 | /generate-availability 2 | /contexts.md 3 | -------------------------------------------------------------------------------- /scripts/generate-availability/testdata/broken_table.md: -------------------------------------------------------------------------------- 1 | Title 2 | ----- 3 | 4 | ## Sub title 5 | 6 | ### Context availability 7 | 8 | | This | is | test | oops | 9 | |------|-----|------|------| 10 | | aaa | bbb | ccc | ddd | 11 | -------------------------------------------------------------------------------- /scripts/generate-availability/testdata/else_directive.md: -------------------------------------------------------------------------------- 1 | title 2 | ----- 3 | 4 | ### Context availability 5 | 6 | | Workflow key | Context | Special functions | 7 | | ---- | ------- | ----------------- | 8 | | concurrency | {% ifhoge ... %}github, inputs{% else %}foo, bar{% endif %} | | 9 | -------------------------------------------------------------------------------- /scripts/generate-availability/testdata/no_heading.md: -------------------------------------------------------------------------------- 1 | Title 2 | ----- 3 | -------------------------------------------------------------------------------- /scripts/generate-availability/testdata/no_table.md: -------------------------------------------------------------------------------- 1 | Title 2 | ----- 3 | 4 | ## Sub title 5 | 6 | ### Context availability 7 | 8 | There is no table. 9 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/.gitignore: -------------------------------------------------------------------------------- 1 | /generate-popular-actions 2 | /generate-popular-actions.exe 3 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/go/outdated.go: -------------------------------------------------------------------------------- 1 | // Code generated by actionlint/scripts/generate-popular-actions. DO NOT EDIT. 2 | 3 | package actionlint 4 | 5 | // PopularActions is data set of known popular actions. Keys are specs (owner/repo@ref) of actions 6 | // and values are their metadata. 7 | var PopularActions = map[string]*ActionMetadata{} 8 | 9 | // OutdatedPopularActionSpecs is a spec set of known outdated popular actions. The word 'outdated' 10 | // means that the runner used by the action is no longer available such as "node12", "node16". 11 | var OutdatedPopularActionSpecs = map[string]struct{}{ 12 | "rhysd/action-setup-vim@v1.0.0": {}, 13 | } 14 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/broken.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"rhysd/action-setup-vim@v1","metadata":{"name":"Setup Vim","inputs":{"neovim":false, 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/known_outdated.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"actions/checkout@v1","metadata":{"name":"Checkout","inputs":{"clean":{"name":"clean","required":false},"fetch-depth":{"name":"fetch-depth","required":false},"lfs":{"name":"lfs","required":false},"path":{"name":"path","required":false},"ref":{"name":"ref","required":false},"repository":{"name":"repository","required":false},"submodules":{"name":"submodules","required":false},"token":{"name":"token","required":false}},"outputs":null,"skip_inputs":false,"skip_outputs":false,"runs":{"using":"","main":"","pre":"","pre-if":"","post":"","post-if":"","steps":null,"image":"","pre-entrypoint":"","entrypoint":"","post-entrypoint":"","args":null,"env":null}},"outdated":true} 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/no_new_version.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"rhysd/action-setup-vim@v1","metadata":{"name":"Setup Vim","inputs":{"configure-args":{"name":"configure-args","required":false},"neovim":{"name":"neovim","required":false},"token":{"name":"token","required":false},"version":{"name":"version","required":false}},"outputs":{"executable":{"name":"executable"}},"skip_inputs":false,"skip_outputs":false,"runs":{"using":"node20","main":"src/index.js","pre":"","pre-if":"","post":"","post-if":"","steps":null,"image":"","pre-entrypoint":"","entrypoint":"","post-entrypoint":"","args":null,"env":null}},"outdated":false} 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/outdated.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"rhysd/action-setup-vim@v1.0.0","metadata":{"name":"Setup Vim","inputs":{"github-token":{"name":"github-token","required":false},"neovim":{"name":"neovim","required":false},"version":{"name":"version","required":false}},"outputs":{"executable":{"name":"executable"}},"skip_inputs":false,"skip_outputs":false,"runs":{"using":"node12","main":"src/index.js","pre":"","pre-if":"","post":"","post-if":"","steps":null,"image":"","pre-entrypoint":"","entrypoint":"","post-entrypoint":"","args":null,"env":null}},"outdated":true} 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/skip_both.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"rhysd/action-setup-vim@v1","metadata":{"name":"Setup Vim","inputs":{"configure-args":{"name":"configure-args","required":false},"neovim":{"name":"neovim","required":false},"token":{"name":"token","required":false},"version":{"name":"version","required":false}},"outputs":{"executable":{"name":"executable"}},"skip_inputs":true,"skip_outputs":true,"runs":{"using":"node20","main":"src/index.js","pre":"","pre-if":"","post":"","post-if":"","steps":null,"image":"","pre-entrypoint":"","entrypoint":"","post-entrypoint":"","args":null,"env":null}},"outdated":false} 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/skip_inputs.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"rhysd/action-setup-vim@v1","metadata":{"name":"Setup Vim","inputs":{"configure-args":{"name":"configure-args","required":false},"neovim":{"name":"neovim","required":false},"token":{"name":"token","required":false},"version":{"name":"version","required":false}},"outputs":{"executable":{"name":"executable"}},"skip_inputs":true,"skip_outputs":false,"runs":{"using":"node20","main":"src/index.js","pre":"","pre-if":"","post":"","post-if":"","steps":null,"image":"","pre-entrypoint":"","entrypoint":"","post-entrypoint":"","args":null,"env":null}},"outdated":false} 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/jsonl/skip_outputs.jsonl: -------------------------------------------------------------------------------- 1 | {"spec":"rhysd/action-setup-vim@v1","metadata":{"name":"Setup Vim","inputs":{"configure-args":{"name":"configure-args","required":false},"neovim":{"name":"neovim","required":false},"token":{"name":"token","required":false},"version":{"name":"version","required":false}},"outputs":{"executable":{"name":"executable"}},"skip_inputs":false,"skip_outputs":true,"runs":{"using":"node20","main":"src/index.js","pre":"","pre-if":"","post":"","post-if":"","steps":null,"image":"","pre-entrypoint":"","entrypoint":"","post-entrypoint":"","args":null,"env":null}},"outdated":false} 2 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/broken.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v0"], 5 | "next": "v1" 6 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/empty_next_version.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v1"], 5 | "next": "" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/empty_slug.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "", 4 | "tags": ["v1"], 5 | "next": "v2" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/fetch.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v1.3.2"], 5 | "next": "v2" 6 | }, 7 | { 8 | "slug": "rhysd/changelog-from-release", 9 | "path": "/action", 10 | "tags": ["v2.2.2"], 11 | "next": "v3" 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/known_outdated.yaml: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "actions/checkout", 4 | "tags": ["v1"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/new_release.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v0"], 5 | "next": "v1" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/no_new_version.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "skip_inputs": true, 5 | "tags": ["v1"], 6 | "next": "this-is-awesome-new-version" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/outdated.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v1.0.0"] 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/repo_not_found.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/this-action-does-not-exist", 4 | "tags": ["v1"], 5 | "next": "v2" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/skip_both.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v1"], 5 | "next": "this-is-awesome-new-version", 6 | "skip_inputs": true, 7 | "skip_outputs": true 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/skip_inputs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "tags": ["v1"], 5 | "next": "this-is-awesome-new-version", 6 | "skip_inputs": true 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /scripts/generate-popular-actions/testdata/registry/skip_outputs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "slug": "rhysd/action-setup-vim", 4 | "skip_outputs": true, 5 | "tags": ["v1"], 6 | "next": "this-is-awesome-new-version" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /scripts/generate-webhook-events/.gitignore: -------------------------------------------------------------------------------- 1 | /events-that-trigger-workflows.md 2 | /generate-webhook-events 3 | -------------------------------------------------------------------------------- /scripts/generate-webhook-events/testdata/no_heading.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Test 3 | --- 4 | 5 | Wow 6 | 7 | ### `check_run` 8 | 9 | Aaaa aaaa 10 | 11 | | Webhook event payload | Activity types | `GITHUB_SHA` | `GITHUB_REF` | 12 | | --------------------- | -------------- | ------------ | -------------| 13 | | [`check_run`](/webhooks/event-payloads/#check_run) | - `created`
- `rerequested`
- `completed` | Last commit on default branch | Default branch | 14 | -------------------------------------------------------------------------------- /scripts/generate-webhook-events/testdata/no_hook_name_link.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Test 3 | --- 4 | 5 | ## About events that trigger workflows 6 | 7 | ## `check_run` 8 | 9 | oops first cell does not contain [`check_run`](/webhooks/event-payloads/#check_run)! 10 | 11 | | Webhook event payload | Activity types | `GITHUB_SHA` | `GITHUB_REF` | 12 | | --------------------- | -------------- | ------------ | -------------| 13 | | wow | - `created`
- `rerequested`
- `completed` | Last commit on default branch | Default branch | 14 | -------------------------------------------------------------------------------- /scripts/generate-webhook-events/testdata/no_hooks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Test 3 | --- 4 | 5 | Wow 6 | 7 | ## Manual events 8 | 9 | Foo 10 | 11 | ## `workflow_dispatch` 12 | 13 | | Webhook event payload | Activity types | `GITHUB_SHA` | `GITHUB_REF` | 14 | | ------------------ | ------------ | ------------ | ------------------| 15 | | [workflow_dispatch](/webhooks/event-payloads/#workflow_dispatch) | n/a | Last commit on the `GITHUB_REF` branch | Branch that received dispatch | 16 | 17 | ## About events that trigger workflows 18 | 19 | Bar 20 | -------------------------------------------------------------------------------- /scripts/generate-webhook-events/testdata/ok.go: -------------------------------------------------------------------------------- 1 | // Code generated by actionlint/scripts/generate-webhook-events. DO NOT EDIT. 2 | 3 | package actionlint 4 | 5 | // AllWebhookTypes is a table of all webhooks with their types. This variable was generated by 6 | // script at ./scripts/generate-webhook-events based on 7 | // https://github.com/github/docs/blob/main/content/actions/using-workflows/events-that-trigger-workflows.md 8 | var AllWebhookTypes = map[string][]string{ 9 | "check_run": {"created", "rerequested", "completed"}, 10 | "discussion": {"opened", "edited", "deleted", "transferred", "pinned", "unpinned", "labeled", "unlabeled", "locked", "unlocked", "category_changed", "answered", "unanswered"}, 11 | "create": {}, 12 | } 13 | -------------------------------------------------------------------------------- /staticcheck.conf: -------------------------------------------------------------------------------- 1 | # https://staticcheck.dev/docs/configuration/ 2 | 3 | # https://staticcheck.dev/docs/checks/ 4 | checks = ["inherit", "ST1016", "ST1020", "ST1021", "ST1022", "ST1000"] 5 | 6 | # vim: ft=toml 7 | -------------------------------------------------------------------------------- /testdata/action_metadata/action-yaml/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | name: 7 | description: your name 8 | default: anonymous 9 | message: 10 | description: message to this action 11 | required: true 12 | addition: 13 | description: additional information 14 | required: false 15 | 16 | outputs: 17 | user_id: 18 | description: user ID 19 | 20 | runs: 21 | using: 'node20' 22 | main: 'index.js' 23 | -------------------------------------------------------------------------------- /testdata/action_metadata/action-yml/action.yml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | name: 7 | description: your name 8 | default: anonymous 9 | message: 10 | description: message to this action 11 | required: true 12 | addition: 13 | description: additional information 14 | required: false 15 | 16 | outputs: 17 | user_id: 18 | description: user ID 19 | 20 | runs: 21 | using: 'node20' 22 | main: 'index.js' 23 | -------------------------------------------------------------------------------- /testdata/action_metadata/branding/action.yml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | branding: 5 | icon: edit 6 | color: white 7 | 8 | inputs: 9 | name: 10 | description: your name 11 | default: anonymous 12 | message: 13 | description: message to this action 14 | required: true 15 | addition: 16 | description: additional information 17 | required: false 18 | 19 | outputs: 20 | user_id: 21 | description: user ID 22 | 23 | runs: 24 | using: 'node20' 25 | main: 'index.js' 26 | -------------------------------------------------------------------------------- /testdata/action_metadata/broken/action.yaml: -------------------------------------------------------------------------------- 1 | author: 'rhysd ' 2 | description: foo: 3 | -------------------------------------------------------------------------------- /testdata/action_metadata/composite/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | runs: 6 | using: composite 7 | steps: 8 | - run: echo hello 9 | -------------------------------------------------------------------------------- /testdata/action_metadata/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | -------------------------------------------------------------------------------- /testdata/action_metadata/docker/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | runs: 6 | using: docker 7 | image: Dockerfile 8 | -------------------------------------------------------------------------------- /testdata/action_metadata/empty/action.yml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | runs: 5 | using: 'node20' 6 | main: 'index.js' 7 | -------------------------------------------------------------------------------- /testdata/action_metadata/input-duplicate/action.yml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | foo: 7 | description: ... 8 | FOO: 9 | description: ... 10 | 11 | runs: 12 | using: 'node20' 13 | main: 'index.js' 14 | -------------------------------------------------------------------------------- /testdata/action_metadata/output-duplicate/action.yml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | outputs: 6 | foo: 7 | description: ... 8 | FOO: 9 | description: ... 10 | 11 | runs: 12 | using: 'node20' 13 | main: 'index.js' 14 | -------------------------------------------------------------------------------- /testdata/action_metadata/uppercase/action.yml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | NAME: 7 | description: your name 8 | default: anonymous 9 | MESSAGE: 10 | description: message to this action 11 | required: true 12 | ADDITION: 13 | description: additional information 14 | required: false 15 | 16 | outputs: 17 | USER_ID: 18 | description: user ID 19 | 20 | runs: 21 | using: 'node20' 22 | main: 'index.js' 23 | -------------------------------------------------------------------------------- /testdata/bench/minimal.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo hi 8 | -------------------------------------------------------------------------------- /testdata/bench/small.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | unit-tests: 6 | strategy: 7 | matrix: 8 | os: [ubuntu-latest, macos-latest, windows-latest] 9 | runs-on: ${{ matrix.os }} 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-go@v5 13 | - run: go test -v -race 14 | lint: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-go@v5 19 | - run: go get honnef.co/go/tools/cmd/staticcheck@latest 20 | - run: | 21 | "$(go env GOPATH)/bin/staticcheck" ./... 22 | -------------------------------------------------------------------------------- /testdata/config/broken.yml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 42 2 | -------------------------------------------------------------------------------- /testdata/config/ok.yml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 2 | labels: 3 | - foo 4 | - bar 5 | -------------------------------------------------------------------------------- /testdata/config/projects/err/.github/actionlint.yaml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 42 2 | -------------------------------------------------------------------------------- /testdata/config/projects/err/.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 7 | -------------------------------------------------------------------------------- /testdata/config/projects/none/.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 7 | -------------------------------------------------------------------------------- /testdata/config/projects/ok/.github/actionlint.yaml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 2 | labels: [] 3 | config-variables: null 4 | -------------------------------------------------------------------------------- /testdata/config/projects/ok/.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 7 | -------------------------------------------------------------------------------- /testdata/config/projects/yml/.github/actionlint.yml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 2 | labels: [] 3 | config-variables: null 4 | -------------------------------------------------------------------------------- /testdata/config/projects/yml/.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 7 | -------------------------------------------------------------------------------- /testdata/err/.github/workflows/called-workflow.yml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo hello 9 | -------------------------------------------------------------------------------- /testdata/err/assign_expression.out: -------------------------------------------------------------------------------- 1 | test.yaml:11:19: expecting a single ${{...}} expression or boolean literal "true" or "false", but found plain text node [syntax-check] 2 | test.yaml:20:21: expecting a single ${{...}} expression or integer literal, but found plain text node [syntax-check] 3 | test.yaml:24:22: expecting a single ${{...}} expression or float number literal, but found plain text node [syntax-check] 4 | -------------------------------------------------------------------------------- /testdata/err/cron_5minutes_limit.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:13: scheduled job runs too frequently. it runs once per 240 seconds. the shortest interval is once every 5 minutes [events] 2 | -------------------------------------------------------------------------------- /testdata/err/cron_5minutes_limit.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | # It's OK. The interval can be every 5 minutes. 4 | - cron: '*/5 * * * *' 5 | # It's bad. The interval can't be less than every 5 minutes. 6 | - cron: '*/4 * * * *' 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ... 13 | -------------------------------------------------------------------------------- /testdata/err/duplicate_keys.out: -------------------------------------------------------------------------------- 1 | test.yaml:9:9: key "FOO" is duplicated in "matrix" section. previously defined at line:8,col:9. note that this key is case insensitive [syntax-check] 2 | test.yaml:12:5: key "runs-on" is duplicated in "test" job. previously defined at line:11,col:5 [syntax-check] 3 | -------------------------------------------------------------------------------- /testdata/err/duplicate_keys.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | # Duplicate case-insensitive keys 8 | foo: [a, b, c] 9 | FOO: [a, b, c] 10 | # Duplicate case-sensitive keys 11 | runs-on: ubuntu-latest 12 | runs-on: ubuntu-latest 13 | steps: 14 | - run: echo 15 | -------------------------------------------------------------------------------- /testdata/err/empty.out: -------------------------------------------------------------------------------- 1 | test.yaml:1:1: workflow is empty [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/err/empty.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/err/empty.yaml -------------------------------------------------------------------------------- /testdata/err/empty_on.out: -------------------------------------------------------------------------------- 1 | test.yaml:1:4: string should not be empty [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/err/empty_on.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo hi 8 | -------------------------------------------------------------------------------- /testdata/err/env_context_banned.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:15: context "env" is not allowed here. available contexts are "github", "inputs", "secrets", "vars". see https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability for more details [expression] 2 | test.yaml:14:19: context "env" is not allowed here. available contexts are "github", "inputs", "matrix", "needs", "secrets", "strategy", "vars". see https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability for more details [expression] 3 | -------------------------------------------------------------------------------- /testdata/err/evaluated_template.out: -------------------------------------------------------------------------------- 1 | test.yaml:22:20: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type object [expression] 2 | test.yaml:22:38: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type {cache-hit: string} [expression] 3 | test.yaml:22:63: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type array [expression] 4 | test.yaml:24:20: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type null [expression] 5 | -------------------------------------------------------------------------------- /testdata/err/expr_check_in_env_var_name.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | env: 4 | ${{ runner.name }}: '' 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | container: 10 | image: node:14.16 11 | env: 12 | ${{ runner.foooooo }}: '' 13 | env: 14 | ${{ runner.fooooooo }}: '' 15 | steps: 16 | - run: echo "$Linux" 17 | env: 18 | ${{ runner.name }}: '' 19 | -------------------------------------------------------------------------------- /testdata/err/expr_check_in_matrix_row_assign.out: -------------------------------------------------------------------------------- 1 | test.yaml:12:23: receiver of object dereference "foo" must be type of object but got "number" [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/expr_check_in_matrix_row_assign.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | error: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | test: 9 | - ${{ github.retention_days }} 10 | steps: 11 | # ERROR: `matrix.test` is a number value 12 | - run: echo ${{ matrix.test.foo }} 13 | no-error: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | # Type of matrix.test falls back to any since number and bool are merged 17 | matrix: 18 | test: 19 | - ${{ 123 }} 20 | - ${{ true }} 21 | steps: 22 | # OK: matrix.test is any type 23 | - run: echo ${{ matrix.test.foo }} 24 | -------------------------------------------------------------------------------- /testdata/err/expr_check_in_services.out: -------------------------------------------------------------------------------- 1 | test.yaml:9:15: "services" section is scalar node but mapping node is expected [syntax-check] 2 | test.yaml:14:15: type of expression at "services" must be object but found type string [expression] 3 | -------------------------------------------------------------------------------- /testdata/err/expr_check_in_services.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | ok1: 4 | services: ${{ fromJSON('{}') }} 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 8 | err1: 9 | services: redis 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo 13 | err2: 14 | services: ${{ 'redis' }} 15 | runs-on: ubuntu-latest 16 | steps: 17 | - run: echo 18 | -------------------------------------------------------------------------------- /testdata/err/expr_in_default_input.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:22: property "input2" is not defined in object type {} [expression] 2 | test.yaml:15:22: property "input3" is not defined in object type {input1: string; input2: string} [expression] 3 | test.yaml:19:18: type of input "input4" must be bool but found type string [expression] 4 | test.yaml:23:18: type of input "input5" must be number but found type string [expression] 5 | -------------------------------------------------------------------------------- /testdata/err/github_script_untrusted_input.out: -------------------------------------------------------------------------------- 1 | test.yaml:11:162: "github.event.head_commit.author.name" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/github_script_untrusted_input.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | issues: 3 | types: [opened] 4 | 5 | jobs: 6 | comment: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/github-script@v7 10 | with: 11 | script: | 12 | github.issues.createComment({ 13 | issue_number: context.issue.number, 14 | owner: context.repo.owner, 15 | repo: context.repo.repo, 16 | body: 'Hello, ${{github.event.head_commit.author.name}}!' 17 | }) 18 | -------------------------------------------------------------------------------- /testdata/err/glob_more.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - '**' 5 | - 'release/v12' 6 | - '[x]' 7 | - ' ' 8 | tags: 9 | - '!*' 10 | - /v\d+/ 11 | - v[0- 12 | - v[9-0] 13 | paths: 14 | - '!' 15 | - 'foo\bar' 16 | - ' ' 17 | - ' foo' 18 | - 'foo ' 19 | - 20 | 21 | jobs: 22 | test: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - run: echo hi 26 | -------------------------------------------------------------------------------- /testdata/err/inputs_without_workflow_call_event.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:23: property "some_input" is not defined in object type {} [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/inputs_without_workflow_call_event.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-20.04 6 | steps: 7 | - run: echo ${{ inputs.some_input }} 8 | -------------------------------------------------------------------------------- /testdata/err/invalid_comparisons.out: -------------------------------------------------------------------------------- 1 | test.yaml:16:17: "string" value cannot be compared to "{}" value with "==" operator [expression] 2 | test.yaml:18:17: "number" value cannot be compared to "array" value with "!=" operator [expression] 3 | test.yaml:20:17: "array" value cannot be compared to "array<{}>" value with "==" operator [expression] 4 | test.yaml:22:17: "number" value cannot be compared to "null" value with ">" operator [expression] 5 | test.yaml:24:17: "bool" value cannot be compared to "bool" value with "<" operator [expression] 6 | test.yaml:26:17: "string" value cannot be compared to "{}" value with ">=" operator [expression] 7 | test.yaml:28:17: "bool" value cannot be compared to "array" value with "<=" operator [expression] 8 | -------------------------------------------------------------------------------- /testdata/err/invalid_event_filters.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | merge_group: 3 | types: opened 4 | paths: /path/to/foo.txt 5 | paths-ignore: /path/to/bar.txt 6 | tags: v*.*.* 7 | tags-ignore: deploy/* 8 | pull_request_review: 9 | types: submitted 10 | paths: /path/to/foo.txt 11 | paths-ignore: /path/to/bar.txt 12 | branches: main 13 | branches-ignore: test 14 | tags: v*.*.* 15 | tags-ignore: deploy/* 16 | pull_request: 17 | tags: v*.*.* 18 | tags-ignore: deploy/* 19 | 20 | jobs: 21 | test: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - run: echo hello 25 | -------------------------------------------------------------------------------- /testdata/err/invalid_float_at_timeout_minutes.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:26: expected scalar node for float value but found scalar node with "!!bool" tag [syntax-check] 2 | test.yaml:10:26: expecting a single ${{...}} expression or float number literal, but found plain text node [syntax-check] 3 | test.yaml:12:26: value at "timeout-minutes" must be greater than zero: 0 [syntax-check] 4 | test.yaml:14:26: value at "timeout-minutes" must be greater than zero: -3.5 [syntax-check] 5 | -------------------------------------------------------------------------------- /testdata/err/invalid_float_at_timeout_minutes.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 'invalid type 1' 8 | timeout-minutes: true 9 | - run: echo 'invalid type 2' 10 | timeout-minutes: '3.5' 11 | - run: echo 'invalid value 1' 12 | timeout-minutes: 0 13 | - run: echo 'invalid value 2' 14 | timeout-minutes: -3.5 15 | - run: echo 'ok 1' 16 | timeout-minutes: 3.5 17 | - run: echo 'ok 2' 18 | timeout-minutes: 3 19 | - run: echo 'ok 3' 20 | timeout-minutes: ${{ 3.5 }} 21 | -------------------------------------------------------------------------------- /testdata/err/invalid_id.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | -foo: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 'must start with _ or letters' 7 | id: -foo 8 | v1.2.3: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: echo 'must contain only alnum or _ or -' 12 | id: v1.2.3 13 | 1-2-3: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - run: echo 'must not start with number' 17 | id: 1-2-3 18 | test: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - run: echo 'must not be empty' 22 | id: "" 23 | -------------------------------------------------------------------------------- /testdata/err/invalid_int_at_max_parallel.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:21: expecting a single ${{...}} expression or integer literal, but found plain text node [syntax-check] 2 | test.yaml:13:21: expected scalar node for integer value but found scalar node with "!!float" tag [syntax-check] 3 | test.yaml:19:21: expecting a single ${{...}} expression or integer literal, but found plain text node [syntax-check] 4 | test.yaml:25:21: value at "max-parallel" must be greater than zero: 0 [syntax-check] 5 | test.yaml:31:21: value at "max-parallel" must be greater than zero: -4 [syntax-check] 6 | -------------------------------------------------------------------------------- /testdata/err/invalid_runner_labels.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:4:14: label "ubuntu-oldest" is unknown\. available labels are .+\. if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file \[runner-label\]/ 2 | test.yaml:8:30: label "windows-latest" conflicts with label "ubuntu-latest" defined at line:8,col:15. note: to run your job on each workers, use matrix [runner-label] 3 | test.yaml:8:46: label "macos-latest" conflicts with label "ubuntu-latest" defined at line:8,col:15. note: to run your job on each workers, use matrix [runner-label] 4 | -------------------------------------------------------------------------------- /testdata/err/invalid_runner_labels.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test1: 4 | runs-on: ubuntu-oldest 5 | steps: 6 | - run: echo 7 | test2: 8 | runs-on: [ubuntu-latest, windows-latest, macos-latest] 9 | steps: 10 | - run: echo 11 | -------------------------------------------------------------------------------- /testdata/err/invalid_steps.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test1: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # 'run' is first 8 | - run: echo hello 9 | uses: actions/checkout@v4 10 | # 'uses' is first 11 | - uses: actions/checkout@v4 12 | run: echo hello 13 | # 'shell' is specified so it must be 'run' 14 | - shell: bash 15 | uses: actions/checkout@v4 16 | # Neither 'run' nor 'uses' is used 17 | - null 18 | test2: 19 | runs-on: ubuntu-latest 20 | # Empty steps 21 | steps: 22 | -------------------------------------------------------------------------------- /testdata/err/issue102.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:3: "runs-on" section is missing in job "actionlint" [syntax-check] 2 | test.yaml:7:9: "uses" is required to run action in step [syntax-check] 3 | -------------------------------------------------------------------------------- /testdata/err/issue102.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | actionlint: 5 | steps: 6 | - 7 | with: 8 | info: test 9 | -------------------------------------------------------------------------------- /testdata/err/issue151_child_of_child_job.out: -------------------------------------------------------------------------------- 1 | test.yaml:26:31: property "first" is not defined in object type {second: {outputs: {second: string}; result: string}} [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/issue155_env_in_job_level_if.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | # Issue #155: `env` should not be available in `jobs..if` 4 | # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability 5 | 6 | jobs: 7 | test1: 8 | runs-on: ubuntu-latest 9 | if: ${{ env.FOO == 'aaa' }} 10 | steps: 11 | - run: echo 'hello' 12 | test2: 13 | runs-on: ubuntu-latest 14 | if: env.FOO == 'aaa' 15 | steps: 16 | - run: echo 'hello' 17 | test3: 18 | uses: org/repo/workflow.yml@v1 19 | if: ${{ env.FOO == 'aaa' }} 20 | test4: 21 | uses: org/repo/workflow.yml@v1 22 | if: env.FOO == 'aaa' 23 | -------------------------------------------------------------------------------- /testdata/err/issue170_empty_permissions.out: -------------------------------------------------------------------------------- 1 | test.yaml:12:17: string should not be empty [syntax-check] 2 | test.yaml:12:17: "" is invalid for permission for all the scopes. available values are "read-all" and "write-all" [permissions] 3 | -------------------------------------------------------------------------------- /testdata/err/issue170_empty_permissions.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | permissions: {} 4 | 5 | jobs: 6 | job1: 7 | permissions: {} 8 | runs-on: ubuntu-latest 9 | steps: 10 | - run: echo '1' 11 | job2: 12 | permissions: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: echo '2' 16 | -------------------------------------------------------------------------------- /testdata/err/issue193.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:42: got unexpected character '"' while lexing expression, expecting 'a'..'z', 'A'..'Z', '_', '0'..'9', ''', '}', '(', ')', '[', ']', '.', '!', '<', '>', '=', '&', '|', '*', ',', ' '. do you mean string literals? only single quotes are available for string delimiter [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/issue193.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | foo: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | continue-on-error: ${{ env.OS == "macos-latest" }} 9 | -------------------------------------------------------------------------------- /testdata/err/issue207_work_dir_with_uses.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:28: "working-directory" is not available with "uses". it is only available with "run" [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/err/issue207_work_dir_with_uses.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | foo: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | working-directory: ./foo 9 | - run: echo "$(pwd)" 10 | working-directory: ./foo 11 | -------------------------------------------------------------------------------- /testdata/err/macos12_runner.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:5:14: label "macos-12" is unknown\. .+/ 2 | -------------------------------------------------------------------------------- /testdata/err/macos12_runner.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: macos-12 6 | steps: 7 | - run: echo 'macOS 12 is no longer supported' 8 | -------------------------------------------------------------------------------- /testdata/err/macos_10.15_removed.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:5:14: label "macos-10\.15" is unknown\. available labels are .+ \[runner-label\]/ 2 | /test\.yaml:9:14: label "macos-10" is unknown\. available labels are .+ \[runner-label\]/ 3 | -------------------------------------------------------------------------------- /testdata/err/macos_10.15_removed.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | macos-10-15: 5 | runs-on: macos-10.15 6 | steps: 7 | - run: echo test 8 | macos-10: 9 | runs-on: macos-10 10 | steps: 11 | - run: echo test 12 | -------------------------------------------------------------------------------- /testdata/err/minimal_cycle_in_needs.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:3: cyclic dependencies in "needs" job configurations are detected. detected cycle is "from" -> "to" -> "from" [job-needs] 2 | -------------------------------------------------------------------------------- /testdata/err/minimal_cycle_in_needs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | # Minimal 4 | from: 5 | needs: [to] 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo 'from' 9 | to: 10 | needs: [from] 11 | runs-on: ubuntu-latest 12 | steps: 13 | - run: echo 'to' 14 | -------------------------------------------------------------------------------- /testdata/err/missing_jobs.out: -------------------------------------------------------------------------------- 1 | test.yaml:2:1: "jobs" section is missing in workflow [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/err/missing_jobs.yaml: -------------------------------------------------------------------------------- 1 | # issue #232 2 | on: push 3 | -------------------------------------------------------------------------------- /testdata/err/missing_on.out: -------------------------------------------------------------------------------- 1 | test.yaml:3:1: "on" section is missing in workflow [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/err/missing_on.yaml: -------------------------------------------------------------------------------- 1 | # issue #232 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo hi 8 | -------------------------------------------------------------------------------- /testdata/err/missing_required_keys.out: -------------------------------------------------------------------------------- 1 | test.yaml:5:7: "type" is missing at "foo" input of workflow_call event [syntax-check] 2 | test.yaml:8:7: "value" is missing at "foo" output of workflow_call event [syntax-check] 3 | test.yaml:10:10: "defaults" section should not be empty. please remove this section if it's unnecessary [syntax-check] 4 | test.yaml:10:10: "defaults" section should have "run" section [syntax-check] 5 | test.yaml:12:1: group name is missing in "concurrency" section [syntax-check] 6 | test.yaml:17:3: "steps" section is missing in job "test" [syntax-check] 7 | test.yaml:17:3: "runs-on" section is missing in job "test" [syntax-check] 8 | test.yaml:18:5: name is missing in "environment" section [syntax-check] 9 | -------------------------------------------------------------------------------- /testdata/err/missing_required_keys.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | # 'type' is missing 5 | foo: 6 | outputs: 7 | # 'value' is missing 8 | foo: 9 | 10 | defaults: 11 | 12 | concurrency: 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | # 'runs-on' and 'steps' are missing 17 | test: 18 | environment: 19 | url: https://example.com 20 | -------------------------------------------------------------------------------- /testdata/err/nested_untrusted_input.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: pull_request 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo ${{ github.event.pages[github.event.commits[github.event.issue.title].author.name].page_name }} 8 | -------------------------------------------------------------------------------- /testdata/err/object_at_runner_label.out: -------------------------------------------------------------------------------- 1 | test.yaml:10:14: type of expression at "runs-on" must be string or array but found type "{foo: string}" [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/object_at_runner_label.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | build: 5 | strategy: 6 | matrix: 7 | x: 8 | - foo: a 9 | - foo: c 10 | runs-on: ${{ matrix.x }} 11 | steps: 12 | - run: echo hi 13 | -------------------------------------------------------------------------------- /testdata/err/one_error.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:41: "github.event.head_commit.message" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions for more details [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/one_error.yaml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo "Checking commit '${{ github.event.head_commit.message }}'" 7 | -------------------------------------------------------------------------------- /testdata/err/outdated_actions.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:15: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue [action] 2 | test.yaml:10:15: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue [action] 3 | -------------------------------------------------------------------------------- /testdata/err/outdated_actions.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # node12 runner 8 | - uses: actions/checkout@v2 9 | # node16 runner 10 | - uses: actions/checkout@v3 11 | -------------------------------------------------------------------------------- /testdata/err/outdated_popular_action.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:15: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue [action] 2 | test.yaml:10:15: the runner of "actions/stale@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue [action] 3 | -------------------------------------------------------------------------------- /testdata/err/outdated_popular_action.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # ERROR: This version is outdated 8 | - uses: actions/checkout@v2 9 | # ERROR: This version is outdated 10 | - uses: actions/stale@v4 11 | -------------------------------------------------------------------------------- /testdata/err/outputs_map_object.out: -------------------------------------------------------------------------------- 1 | /{string => string}/ 2 | -------------------------------------------------------------------------------- /testdata/err/outputs_map_object.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - uses: ./foo/bar 7 | id: foo 8 | # Check if the 'outputs' object is typed as {string => string} 9 | - run: echo ${{ steps.foo.outputs }} 10 | -------------------------------------------------------------------------------- /testdata/err/outputs_of_action_skipping_inputs_check.out: -------------------------------------------------------------------------------- 1 | test.yaml:16:40: property "this_output_does_not_exist" is not defined in object type {data: string; headers: string; status: string} [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/outputs_of_action_skipping_inputs_check.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | logLatestRelease: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: octokit/request-action@v2.x 8 | id: get_latest_release 9 | with: 10 | route: GET /repos/{owner}/{repo}/releases/latest 11 | owner: octokit 12 | repo: request-action 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | # octkit/request-action skips inputs check, but outputs are still checked 16 | - run: "echo latest release: ${{ steps.get_latest_release.outputs.this_output_does_not_exist }}" 17 | -------------------------------------------------------------------------------- /testdata/err/permissions_upper_case.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:3: unknown permission scope "ACTIONS". all available permission scopes are "actions", "attestations", "checks", "contents", "deployments", "discussions", "id-token", "issues", "packages", "pages", "pull-requests", "repository-projects", "security-events", "statuses" [permissions] 2 | test.yaml:5:3: unknown permission scope "CHECKS". all available permission scopes are "actions", "attestations", "checks", "contents", "deployments", "discussions", "id-token", "issues", "packages", "pages", "pull-requests", "repository-projects", "security-events", "statuses" [permissions] 3 | -------------------------------------------------------------------------------- /testdata/err/permissions_upper_case.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | permissions: 4 | ACTIONS: read 5 | CHECKS: write 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: echo hi 12 | -------------------------------------------------------------------------------- /testdata/err/pyflakes_job_default_shell.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:9:9: pyflakes reported issue in this script: .+ \[pyflakes\]/ 2 | -------------------------------------------------------------------------------- /testdata/err/pyflakes_job_default_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | job-level: 4 | runs-on: ubuntu-latest 5 | defaults: 6 | run: 7 | shell: python 8 | steps: 9 | - run: print(unknown_var) 10 | -------------------------------------------------------------------------------- /testdata/err/pyflakes_step_shell.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:7:9: pyflakes reported issue in this script: .+ \[pyflakes\]/ 2 | /test\.yaml:10:9: pyflakes reported issue in this script: .+ \[pyflakes\]/ 3 | /test\.yaml:13:9: pyflakes reported issue in this script: .+ \[pyflakes\]/ 4 | -------------------------------------------------------------------------------- /testdata/err/pyflakes_step_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | step-level: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # Lint check failed. The error is output to stdout (#411) 7 | - run: print(unknown_var) 8 | shell: python 9 | # Syntax check failed. The error is output to stderr (#411) 10 | - run: print( 11 | shell: python 12 | # Check custom shell 13 | - run: print(unknown_var) 14 | shell: 'python {0}' 15 | -------------------------------------------------------------------------------- /testdata/err/pyflakes_workflow_default_shell.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:11:9: pyflakes reported issue in this script: .+ \[pyflakes\]/ 2 | -------------------------------------------------------------------------------- /testdata/err/pyflakes_workflow_default_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | defaults: 4 | run: 5 | shell: python 6 | 7 | jobs: 8 | job-level: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: print(unknown_var) 12 | -------------------------------------------------------------------------------- /testdata/err/random_order_cycle_in_needs.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:3: cyclic dependencies in "needs" job configurations are detected. detected cycle is "a" -> "b" -> "c" -> "d" -> "a" [job-needs] 2 | -------------------------------------------------------------------------------- /testdata/err/random_order_cycle_in_needs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | # Order of jobs is random 4 | a: 5 | needs: [b] 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo 'a' 9 | c: 10 | needs: [d] 11 | runs-on: ubuntu-latest 12 | steps: 13 | - run: echo 'd' 14 | d: 15 | needs: [a] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - run: echo 'a' 19 | b: 20 | needs: [c] 21 | runs-on: ubuntu-latest 22 | steps: 23 | - run: echo 'b' 24 | -------------------------------------------------------------------------------- /testdata/err/reusable_workflow_empty_secrets.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:12:23: property "calling_workflow_secret" is not defined in object type {.+} \[expression\]/ 2 | -------------------------------------------------------------------------------- /testdata/err/reusable_workflow_empty_secrets.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | # actionlint assumes this workflow takes no secret value. 4 | secrets: 5 | 6 | jobs: 7 | pass-secret-to-action: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Use a repo or org secret from the calling workflow. 11 | # So referring this secret causes an error 12 | run: echo ${{ secrets.CALLING_WORKFLOW_SECRET }} 13 | -------------------------------------------------------------------------------- /testdata/err/run_name_check_expr.out: -------------------------------------------------------------------------------- 1 | test.yaml:2:25: undefined variable "hoge". available variables are "env", "github", "inputs", "job", "matrix", "needs", "runner", "secrets", "steps", "strategy", "vars" [expression] 2 | -------------------------------------------------------------------------------- /testdata/err/run_name_check_expr.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | run-name: Deploy to ${{ hoge }} 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo hello 9 | -------------------------------------------------------------------------------- /testdata/err/runner_labels_conflict_matrix.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:14: label "windows-latest" conflicts with label "ubuntu-latest" defined at line:7,col:15. note: to run your job on each workers, use matrix [runner-label] 2 | test.yaml:6:30: label "macos-latest" conflicts with label "ubuntu-latest" defined at line:7,col:15. note: to run your job on each workers, use matrix [runner-label] 3 | test.yaml:6:44: label "windows" conflicts with label "ubuntu-latest" defined at line:7,col:15. note: to run your job on each workers, use matrix [runner-label] 4 | -------------------------------------------------------------------------------- /testdata/err/runner_labels_conflict_matrix.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | os: [windows-latest, macos-latest, windows] 7 | runs-on: [ubuntu-latest, '${{matrix.os}}'] 8 | steps: 9 | - run: echo ... 10 | -------------------------------------------------------------------------------- /testdata/err/shell_key_context_availability.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:6:16: context "env" is not allowed here\. no context is available here\. .+ \[expression\]/ 2 | /test\.yaml:18:20: context "env" is not allowed here\. no context is available here\. .+ \[expression\]/ 3 | -------------------------------------------------------------------------------- /testdata/err/shell_key_context_availability.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | defaults: 4 | run: 5 | # ERROR: No context is available here 6 | shell: ${{ env.SHELL }} 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | defaults: 12 | run: 13 | # OK: 'env' context is available here 14 | shell: ${{ env.SHELL }} 15 | steps: 16 | - run: echo hello 17 | # ERROR: No context is available here 18 | shell: ${{ env.SHELL}} 19 | -------------------------------------------------------------------------------- /testdata/err/variables_type_check.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:24: receiver of object dereference "bar" must be type of object but got "string" [expression] 2 | test.yaml:9:20: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type {string => string} [expression] 3 | -------------------------------------------------------------------------------- /testdata/err/variables_type_check.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo "${{ vars.FOO.bar }}" 9 | - run: echo "${{ vars }}" 10 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_inputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:9:18: input "input_not_ok" of workflow_call event has the default value "a", but it is also required. if an input is marked as required, its default value will never be used [events] 2 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_inputs.yaml: -------------------------------------------------------------------------------- 1 | # Note: Added at #154 2 | on: 3 | workflow_call: 4 | inputs: 5 | input_not_ok: 6 | description: test 7 | type: string 8 | required: true # set this input is required 9 | default: a # but default value is provided 10 | input_ok_1: 11 | description: test 12 | type: string 13 | default: a 14 | input_ok_2: 15 | description: test 16 | type: string 17 | required: true 18 | input_ok_3: 19 | description: test 20 | type: string 21 | 22 | jobs: 23 | test: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - run: echo hello 27 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_invalid_secrets.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:14: expected mapping node for secrets or "inherit" string node but found "foo" node [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_invalid_secrets.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | 4 | jobs: 5 | pass-secrets-to-workflow: 6 | uses: ./.github/workflows/called-workflow.yml 7 | secrets: foo 8 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_outputs_sema.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:20: property "some_output" is not defined in object type {} [expression] 2 | test.yaml:9:20: property "unknown_output" is not defined in object type {foo: string} [expression] 3 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_outputs_sema.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | outputs: 4 | output1: 5 | description: "no outputs" 6 | value: ${{ jobs.job0.outputs.some_output }} 7 | output2: 8 | description: "unknown output" 9 | value: ${{ jobs.job1.outputs.unknown_output }} 10 | jobs: 11 | job0: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - run: echo hi 15 | job1: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | foo: "${{ steps.hello.outputs.foo }}" 19 | steps: 20 | - run: echo hello 21 | id: hello 22 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_outputs_syntax.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:7: "value" is missing at "missing-value" output of workflow_call event [syntax-check] 2 | test.yaml:8:7: "value" is missing at "missing-all" output of workflow_call event [syntax-check] 3 | test.yaml:12:9: unexpected key "unknown-section" for "outputs at workflow_call event" section. expected one of "description", "value" [syntax-check] 4 | test.yaml:16:7: key "duplicate-key" is duplicated in "outputs" section. previously defined at line:13,col:7. note that this key is case insensitive [syntax-check] 5 | test.yaml:21:15: string should not be empty [syntax-check] 6 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_secrets.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:14:23: property "secret1" is not defined in object type {.*secret0: string.*}/ 2 | -------------------------------------------------------------------------------- /testdata/err/workflow_call_secrets.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | secrets: 4 | secret0: 5 | description: 'test' 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-20.04 10 | steps: 11 | # OK 12 | - run: echo ${{ secrets.secret0 }} 13 | # ERROR 14 | - run: echo ${{ secrets.secret1 }} 15 | -------------------------------------------------------------------------------- /testdata/err/workflow_dispatch_input_types_2.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:15: input type of workflow_dispatch event must be one of "string", "number", "boolean", "choice", "environment" but got "unknown" [syntax-check] 2 | test.yaml:9:18: type of "number_invalid_default" input is "number" but its default value "hello" cannot be parsed as a float number: strconv.ParseFloat: parsing "hello": invalid syntax [events] 3 | -------------------------------------------------------------------------------- /testdata/err/workflow_dispatch_input_types_2.yaml: -------------------------------------------------------------------------------- 1 | name: Test for workflow_dispatch input types (part2) 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | input_unknown_type: 6 | type: unknown 7 | number_invalid_default: 8 | type: number 9 | default: hello 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: echo hi 16 | -------------------------------------------------------------------------------- /testdata/err/workflow_dispatch_more_than_10_inputs.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:3:3: maximum number of inputs for "workflow_dispatch" event is 10 but 11 inputs are provided. see .+ \[events\]/ 2 | -------------------------------------------------------------------------------- /testdata/examples/.github/actions/my-action-with-output/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action with output' 2 | author: 'rhysd ' 3 | description: 'my action with outputs' 4 | 5 | outputs: 6 | some_value: 7 | description: some value returned from this action 8 | 9 | runs: 10 | using: 'node20' 11 | main: 'index.js' 12 | -------------------------------------------------------------------------------- /testdata/examples/.github/actions/my-action-with-output/index.js: -------------------------------------------------------------------------------- 1 | console.log(process.env) 2 | -------------------------------------------------------------------------------- /testdata/examples/.github/actions/my-action/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | name: 7 | description: your name 8 | default: anonymous 9 | message: 10 | description: message to this action 11 | required: true 12 | addition: 13 | description: additional information 14 | required: false 15 | 16 | outputs: 17 | user_id: 18 | description: user ID 19 | 20 | runs: 21 | using: 'node20' 22 | main: 'index.js' 23 | -------------------------------------------------------------------------------- /testdata/examples/.github/actions/my-action/index.js: -------------------------------------------------------------------------------- 1 | console.log(process.env) 2 | -------------------------------------------------------------------------------- /testdata/examples/.github/actions/my-invalid-action/action.yml: -------------------------------------------------------------------------------- 1 | # .github/actions/my-invalid-action/action.yml 2 | 3 | name: 'My action' 4 | author: '...' 5 | # ERROR: 'description' section is required 6 | 7 | branding: 8 | # ERROR: Invalid icon name 9 | icon: dog 10 | # ERROR: Unsupported icon color 11 | color: gray-white 12 | 13 | runs: 14 | # ERROR: Node.js runtime version is too old 15 | using: 'node14' 16 | # ERROR: The source file being run by this action does not exist 17 | main: 'this-file-does-not-exist.js' 18 | # ERROR: 'env' configuration is only allowed for Docker actions 19 | env: 20 | SOME_VAR: SOME_VALUE 21 | -------------------------------------------------------------------------------- /testdata/examples/action_metadata_syntax_validation.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # actionlint checks an action when it is actually used in a workflow 8 | - uses: ./.github/actions/my-invalid-action 9 | -------------------------------------------------------------------------------- /testdata/examples/broken_yaml.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:0: could not parse as YAML: yaml: line 6: mapping values are not allowed in this context [syntax-check] 2 | -------------------------------------------------------------------------------- /testdata/examples/broken_yaml.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | linux: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: foo: 7 | -------------------------------------------------------------------------------- /testdata/examples/builtin_func_special_checks.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:18: property "mac" is not defined in object type {linux: string; win: string} [expression] 2 | test.yaml:9:24: format string "{0}{1}" does not contain placeholder {2}. remove argument which is unused in the format string [expression] 3 | test.yaml:11:24: format string "{0}{1}{2}" contains placeholder {2} but only 2 arguments are given to format [expression] 4 | test.yaml:14:31: broken JSON string is passed to fromJSON() at offset 23: unexpected end of JSON input [expression] 5 | -------------------------------------------------------------------------------- /testdata/examples/builtin_func_special_checks.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | # ERROR: Key 'mac' does not exist in the object returned by the fromJSON() 6 | runs-on: ${{ fromJSON('{"win":"windows-latest","linux":"ubuntul-latest"}')['mac'] }} 7 | steps: 8 | # ERROR: {2} is missing in the first argument of format() 9 | - run: echo "${{ format('{0}{1}', 1, 2, 3) }}" 10 | # ERROR: Argument for {2} is missing in the arguments of format() 11 | - run: echo "${{ format('{0}{1}{2}', 1, 2) }}" 12 | - run: echo This is special branch! 13 | # ERROR: Broken JSON string. Special check for fromJSON() 14 | if: contains(fromJson('["main","release","dev"'), github.ref_name) 15 | -------------------------------------------------------------------------------- /testdata/examples/comparison_strict_checks.out: -------------------------------------------------------------------------------- 1 | test.yaml:13:17: "object" value cannot be compared to "string" value with "==" operator [expression] 2 | test.yaml:16:17: "bool" value cannot be compared to "number" value with ">" operator [expression] 3 | -------------------------------------------------------------------------------- /testdata/examples/comparison_strict_checks.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | timeout: 5 | type: boolean 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: echo 'called!' 12 | # ERROR: Comparing string to object is always evaluated to false 13 | if: ${{ github.event == 'workflow_call' }} 14 | - run: echo 'timeout is too long' 15 | # ERROR: Comparing boolean value with `>` doesn't make sense 16 | if: ${{ inputs.timeout > 60 }} 17 | -------------------------------------------------------------------------------- /testdata/examples/contextual_matrix_values.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:19:24: property "platform" is not defined in object type {.+} \[expression\]/ 2 | /test\.yaml:21:24: property "dev" is not defined in object type {.+} \[expression\]/ 3 | test.yaml:34:24: property "os" is not defined in object type {} [expression] 4 | -------------------------------------------------------------------------------- /testdata/examples/contextual_needs_object.out: -------------------------------------------------------------------------------- 1 | test.yaml:16:24: property "prepare" is not defined in object type {} [expression] 2 | test.yaml:26:24: property "foo" is not defined in object type {installed: string} [expression] 3 | test.yaml:28:24: property "some_job" is not defined in object type {install: {outputs: {installed: string}; result: string}; prepare: {outputs: {prepared: string}; result: string}} [expression] 4 | test.yaml:33:24: property "build" is not defined in object type {} [expression] 5 | -------------------------------------------------------------------------------- /testdata/examples/contextual_steps_outputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:10:24: property "get_value" is not defined in object type {} [expression] 2 | test.yaml:22:24: property "get_value" is not defined in object type {} [expression] 3 | -------------------------------------------------------------------------------- /testdata/examples/cron_schedule_check.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:13: invalid CRON format "0 */3 * *" in schedule event: expected exactly 5 fields, found 4: [0 */3 * *] [events] 2 | test.yaml:6:13: scheduled job runs too frequently. it runs once per 60 seconds. the shortest interval is once every 5 minutes [events] 3 | -------------------------------------------------------------------------------- /testdata/examples/cron_schedule_check.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | schedule: 3 | # Cron syntax is not correct 4 | - cron: '0 */3 * *' 5 | # Interval of scheduled job is too small (job runs too frequently) 6 | - cron: '* */3 * * *' 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ... 13 | -------------------------------------------------------------------------------- /testdata/examples/cyclic_deps_needs.out: -------------------------------------------------------------------------------- 1 | test.yaml:3:3: cyclic dependencies in "needs" job configurations are detected. detected cycle is "prepare" -> "build" -> "install" -> "prepare" [job-needs] 2 | -------------------------------------------------------------------------------- /testdata/examples/cyclic_deps_needs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | prepare: 4 | needs: [build] 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 'prepare' 8 | install: 9 | needs: [prepare] 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo 'install' 13 | build: 14 | needs: [install] 15 | runs-on: ubuntu-latest 16 | steps: 17 | - run: echo 'build' 18 | -------------------------------------------------------------------------------- /testdata/examples/deprecated_workflow_commands.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:14: workflow command "set-output" was deprecated. use `echo "{name}={value}" >> $GITHUB_OUTPUT` instead: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions [deprecated-commands] 2 | -------------------------------------------------------------------------------- /testdata/examples/deprecated_workflow_commands.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # ERROR: 'set-output' workflow command was deprecated 8 | - run: echo '::set-output name=foo::bar' 9 | # OK: Use this instead 10 | - run: echo "foo=bar" >> "$GITHUB_OUTPUT" 11 | # OK: 'debug' command is not deprecated 12 | - run: echo "::debug::Set the Octocat variable" 13 | -------------------------------------------------------------------------------- /testdata/examples/detect_outdated_popular_actions.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:15: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue [action] 2 | -------------------------------------------------------------------------------- /testdata/examples/detect_outdated_popular_actions.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # ERROR: actions/checkout@v3 is using the outdated runner 'node16' 8 | - uses: actions/checkout@v3 9 | -------------------------------------------------------------------------------- /testdata/examples/env_var_names.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:7: environment variable name "FOO=BAR" is invalid. '&', '=' and spaces should not be contained [env-var] 2 | test.yaml:7:7: environment variable name "FOO BAR" is invalid. '&', '=' and spaces should not be contained [env-var] 3 | -------------------------------------------------------------------------------- /testdata/examples/env_var_names.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | env: 6 | FOO=BAR: foo 7 | FOO BAR: foo 8 | steps: 9 | - run: echo 'hello' 10 | -------------------------------------------------------------------------------- /testdata/examples/expand_object.out: -------------------------------------------------------------------------------- 1 | test.yaml:19:14: type of expression at "env" must be object but found type string [expression] 2 | -------------------------------------------------------------------------------- /testdata/examples/expand_object.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | env_string: 7 | - 'FOO=BAR' 8 | - 'FOO=PIYO' 9 | env_object: 10 | - FOO: BAR 11 | - FOO: PIYO 12 | runs-on: ubuntu-latest 13 | steps: 14 | # OK: Expanding object at 'env:' section 15 | - run: echo "$FOO" 16 | env: ${{ matrix.env_object }} 17 | # ERROR: String value cannot be expanded as object 18 | - run: echo "$FOO" 19 | env: ${{ matrix.env_string }} 20 | -------------------------------------------------------------------------------- /testdata/examples/expression_syntax_error.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # " is not available for string literal delimiter 7 | - run: echo '${{ "hello" }}' 8 | # + operator does not exist 9 | - run: echo '${{ 1 + 1 }}' 10 | # Missing ')' paren 11 | - run: echo "${{ toJson(hashFiles('**/lock', '**/cache/') }}" 12 | # unexpected end of input 13 | - run: echo '${{ github.event. }}' 14 | -------------------------------------------------------------------------------- /testdata/examples/glob.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | # ^ is not available for branch name. This kind of mistake is usually caused by misunderstanding 5 | # that regular expression is available here 6 | - '^foo-' 7 | tags: 8 | # Invalid syntax. + cannot follow special character * 9 | - 'v*+' 10 | # Invalid character range 9-1 11 | - 'v[9-1]' 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - run: echo ... 18 | -------------------------------------------------------------------------------- /testdata/examples/hardcoded_credentials.out: -------------------------------------------------------------------------------- 1 | test.yaml:10:19: "password" section in "container" section should be specified via secrets. do not put password value directly [credentials] 2 | test.yaml:17:21: "password" section in "redis" service should be specified via secrets. do not put password value directly [credentials] 3 | -------------------------------------------------------------------------------- /testdata/examples/hardcoded_credentials.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | container: 6 | image: 'example.com/owner/image' 7 | credentials: 8 | username: user 9 | # ERROR: Hardcoded password 10 | password: pass 11 | services: 12 | redis: 13 | image: redis 14 | credentials: 15 | username: user 16 | # ERROR: Hardcoded password 17 | password: pass 18 | steps: 19 | - run: echo 'hello' 20 | -------------------------------------------------------------------------------- /testdata/examples/id_naming_convention.out: -------------------------------------------------------------------------------- 1 | test.yaml:5:3: invalid job ID "foo-v1.2.3". job ID must start with a letter or _ and contain only alphanumeric characters, -, or _ [id] 2 | test.yaml:10:13: invalid step ID "echo for test". step ID must start with a letter or _ and contain only alphanumeric characters, -, or _ [id] 3 | test.yaml:12:3: invalid job ID "-hello-world-". job ID must start with a letter or _ and contain only alphanumeric characters, -, or _ [id] 4 | test.yaml:17:3: invalid job ID "2d-game". job ID must start with a letter or _ and contain only alphanumeric characters, -, or _ [id] 5 | -------------------------------------------------------------------------------- /testdata/examples/id_naming_convention.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | # ERROR: '.' cannot be contained in ID 5 | foo-v1.2.3: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo 'job ID with version' 9 | # ERROR: ID cannot contain spaces 10 | id: echo for test 11 | # ERROR: ID cannot start with '-' 12 | -hello-world-: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: echo 'oops' 16 | # ERROR: ID cannot start with numbers 17 | 2d-game: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - run: echo 'oops' 21 | -------------------------------------------------------------------------------- /testdata/examples/if_cond_always_true.out: -------------------------------------------------------------------------------- 1 | test.yaml:16:13: if: condition "${{ github.event_name == 'push' }}\n" is always evaluated to true because extra characters are around ${{ }} [if-cond] 2 | test.yaml:20:13: if: condition "${{ github.event_name == 'push' }} " is always evaluated to true because extra characters are around ${{ }} [if-cond] 3 | test.yaml:26:13: if: condition "${{ github.event_name == 'push' }} && ${{ github.ref_name == 'main' }}" is always evaluated to true because extra characters are around ${{ }} [if-cond] 4 | -------------------------------------------------------------------------------- /testdata/examples/invalid_action_format.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:15: specifying action "actions/checkout" in invalid format because ref is missing. available formats are "{owner}/{repo}@{ref}" or "{owner}/{repo}/{path}@{ref}" [action] 2 | test.yaml:9:15: specifying action "checkout@v2" in invalid format because owner is missing. available formats are "{owner}/{repo}@{ref}" or "{owner}/{repo}/{path}@{ref}" [action] 3 | test.yaml:11:15: tag of Docker action should not be empty: "docker://image" [action] 4 | test.yaml:13:15: specifying action ".github/my-actions/do-something" in invalid format because ref is missing. available formats are "{owner}/{repo}@{ref}" or "{owner}/{repo}/{path}@{ref}" [action] 5 | -------------------------------------------------------------------------------- /testdata/examples/invalid_action_format.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # ERROR: ref is missing 7 | - uses: actions/checkout 8 | # ERROR: owner name is missing 9 | - uses: checkout@v2 10 | # ERROR: tag is empty 11 | - uses: 'docker://image:' 12 | # ERROR: local action must start with './' 13 | - uses: .github/my-actions/do-something 14 | -------------------------------------------------------------------------------- /testdata/examples/invalid_ids_in_needs.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:18: job ID "BAR" duplicates in "needs" section. note that job ID is case insensitive [job-needs] 2 | test.yaml:8:3: job "bar" needs job "unknown" which does not exist in this workflow [job-needs] 3 | -------------------------------------------------------------------------------- /testdata/examples/invalid_ids_in_needs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | foo: 4 | needs: [bar, BAR] 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 'hi' 8 | bar: 9 | needs: [unknown] 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo 'hi' 13 | -------------------------------------------------------------------------------- /testdata/examples/job_step_ids_duplicate.out: -------------------------------------------------------------------------------- 1 | test.yaml:10:13: step ID "STEP_ID" duplicates. previously defined at line:7,col:13. step ID must be unique within a job. note that step ID is case insensitive [id] 2 | test.yaml:12:3: key "TEST" is duplicated in "jobs" section. previously defined at line:3,col:3. note that this key is case insensitive [syntax-check] 3 | -------------------------------------------------------------------------------- /testdata/examples/job_step_ids_duplicate.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 'hello' 7 | id: step_id 8 | - run: echo 'bye' 9 | # ERROR: Duplicate step ID 10 | id: STEP_ID 11 | # ERROR: Duplicate job ID 12 | TEST: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: echo 'hello' 16 | # OK. Step ID uniqueness is job-local 17 | id: step_id 18 | -------------------------------------------------------------------------------- /testdata/examples/local_action_inputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:15: missing input "message" which is required by action "My action" defined at "./.github/actions/my-action". all required inputs are "message" [action] 2 | test.yaml:13:11: input "additions" is not defined in action "My action" defined at "./.github/actions/my-action". available inputs are "addition", "message", "name" [action] 3 | -------------------------------------------------------------------------------- /testdata/examples/local_action_inputs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # missing required input "message" 7 | - uses: ./.github/actions/my-action 8 | # unexpected input "additions" 9 | - uses: ./.github/actions/my-action 10 | with: 11 | name: rhysd 12 | message: hello 13 | additions: foo, bar 14 | -------------------------------------------------------------------------------- /testdata/examples/local_action_outputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:23: property "my_action" is not defined in object type {} [expression] 2 | test.yaml:15:23: property "some-value" is not defined in object type {some_value: string} [expression] 3 | -------------------------------------------------------------------------------- /testdata/examples/local_action_outputs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # ERROR: The step is not yet run 8 | - run: echo ${{ steps.my_action.outputs.some_value }} 9 | # The action runs here and sets its outputs 10 | - uses: ./.github/actions/my-action-with-output 11 | id: my_action 12 | # OK 13 | - run: echo ${{ steps.my_action.outputs.some_value }} 14 | # ERROR: No output named 'some-value' (typo) 15 | - run: echo ${{ steps.my_action.outputs.some-value }} 16 | -------------------------------------------------------------------------------- /testdata/examples/main.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branch: main 4 | tags: 5 | - 'v\d+' 6 | jobs: 7 | test: 8 | strategy: 9 | matrix: 10 | os: [macos-latest, linux-latest] 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - run: echo "Checking commit '${{ github.event.head_commit.message }}'" 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node_version: 18.x 18 | - uses: actions/cache@v4 19 | with: 20 | path: ~/.npm 21 | key: ${{ matrix.platform }}-node-${{ hashFiles('**/package-lock.json') }} 22 | if: ${{ github.repository.permissions.admin == true }} 23 | - run: npm install && npm test 24 | -------------------------------------------------------------------------------- /testdata/examples/matrix_checks.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:28: duplicate value "14" is found in matrix "node". the same value is at line:6,col:24 [matrix] 2 | test.yaml:9:19: value "13" in "exclude" does not match in matrix "node" combinations. possible values are "10", "12", "14", "14" [matrix] 3 | test.yaml:12:13: "platform" in "exclude" section does not exist in matrix. available matrix configurations are "node", "os" [matrix] 4 | -------------------------------------------------------------------------------- /testdata/examples/matrix_checks.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | node: [10, 12, 14, 14] 7 | os: [ubuntu-latest, macos-latest] 8 | exclude: 9 | - node: 13 10 | os: ubuntu-latest 11 | - node: 10 12 | platform: ubuntu-latest 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - run: echo ... 16 | -------------------------------------------------------------------------------- /testdata/examples/missing_required_keys.out: -------------------------------------------------------------------------------- 1 | test.yaml:3:3: "runs-on" section is missing in job "test" [syntax-check] 2 | test.yaml:8:9: key "VERSION_NAME" is duplicated in "matrix" section. previously defined at line:7,col:9. note that this key is case insensitive [syntax-check] 3 | -------------------------------------------------------------------------------- /testdata/examples/missing_required_keys.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | # ERROR: Matrix name is duplicated. These keys are case insensitive 6 | matrix: 7 | version_name: [v1, v2] 8 | VERSION_NAME: [V1, V2] 9 | # ERROR: runs-on is missing 10 | steps: 11 | - run: echo 'hello' 12 | -------------------------------------------------------------------------------- /testdata/examples/not_persistent_matrix_values.out: -------------------------------------------------------------------------------- 1 | test.yaml:22:19: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type array [expression] 2 | -------------------------------------------------------------------------------- /testdata/examples/not_persistent_matrix_values.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | foo: 7 | - 'string value' 8 | - 42 9 | - {aaa: true, bbb: null} 10 | bar: 11 | - [42] 12 | - [true] 13 | - [{aaa: true, bbb: null}] 14 | - [] 15 | runs-on: ubuntu-latest 16 | steps: 17 | # matrix.foo is any type value 18 | - run: echo ${{ matrix.foo }} 19 | # matrix.bar is array type value 20 | - run: echo ${{ matrix.bar[0] }} 21 | # ERROR: Array cannot be evaluated as string 22 | - run: echo ${{ matrix.bar }} 23 | -------------------------------------------------------------------------------- /testdata/examples/permissions.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:14: "write" is invalid for permission for all the scopes. available values are "read-all" and "write-all" [permissions] 2 | test.yaml:11:7: unknown permission scope "check". all available permission scopes are "actions", "attestations", "checks", "contents", "deployments", "discussions", "id-token", "issues", "packages", "pages", "pull-requests", "repository-projects", "security-events", "statuses" [permissions] 3 | test.yaml:13:15: "readable" is invalid for permission of scope "issues". available values are "read", "write" or "none" [permissions] 4 | -------------------------------------------------------------------------------- /testdata/examples/permissions.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | # ERROR: Available values for whole permissions are "write-all", "read-all" or "none" 4 | permissions: write 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | # ERROR: "checks" is correct scope name 11 | check: write 12 | # ERROR: Available values are "read", "write" or "none" 13 | issues: readable 14 | steps: 15 | - run: echo hello 16 | -------------------------------------------------------------------------------- /testdata/examples/popular_action_inputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:15: missing input "key" which is required by action "actions/cache@v4". all required inputs are "key", "path" [action] 2 | /test\.yaml:9:11: input "keys" is not defined in action "actions/cache@v4"\. available inputs are .+ \[action\]/ 3 | -------------------------------------------------------------------------------- /testdata/examples/popular_action_inputs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/cache@v4 8 | with: 9 | keys: | 10 | ${{ hashFiles('**/*.lock') }} 11 | ${{ hashFiles('**/*.cache') }} 12 | path: ./packages 13 | - run: make 14 | -------------------------------------------------------------------------------- /testdata/examples/popular_action_outputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:23: property "cache" is not defined in object type {} [expression] 2 | test.yaml:18:23: property "cache_hit" is not defined in object type {cache-hit: string} [expression] 3 | -------------------------------------------------------------------------------- /testdata/examples/popular_action_outputs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # ERROR: The step is not run yet at this point 8 | - run: echo ${{ steps.cache.outputs.cache-hit }} 9 | # actions/cache sets cache-hit output 10 | - uses: actions/cache@v4 11 | id: cache 12 | with: 13 | key: ${{ hashFiles('**/*.lock') }} 14 | path: ./packages 15 | # OK 16 | - run: echo ${{ steps.cache.outputs.cache-hit }} 17 | # ERROR: Typo at output name 18 | - run: echo ${{ steps.cache.outputs.cache_hit }} 19 | -------------------------------------------------------------------------------- /testdata/examples/pyflakes_integration.out: -------------------------------------------------------------------------------- 1 | test.yaml:10:9: pyflakes reported issue in this script: 1:7: undefined name 'hello' [pyflakes] 2 | test.yaml:19:9: pyflakes reported issue in this script: 2:5: import 'sys' from line 1 shadowed by loop variable [pyflakes] 3 | test.yaml:23:9: pyflakes reported issue in this script: 1:1: 'time.sleep' imported but unused [pyflakes] 4 | -------------------------------------------------------------------------------- /testdata/examples/pyflakes_integration.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | linux: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # Yay! No error 7 | - run: print('${{ runner.os }}') 8 | shell: python 9 | # ERROR: Undefined variable 10 | - run: print(hello) 11 | shell: python 12 | linux2: 13 | runs-on: ubuntu-latest 14 | defaults: 15 | run: 16 | # Run script with Python by default 17 | shell: python 18 | steps: 19 | - run: | 20 | import sys 21 | for sys in ['system1', 'system2']: 22 | print(sys) 23 | - run: | 24 | from time import sleep 25 | print(100) 26 | -------------------------------------------------------------------------------- /testdata/examples/reusable_workflow_outputs.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:20: property "imagetag" is not defined in object type {image_tag: string} [expression] 2 | -------------------------------------------------------------------------------- /testdata/examples/reusable_workflow_outputs.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | outputs: 4 | image-version: 5 | description: "Docker image version" 6 | # ERROR: 'imagetag' does not exist (typo of 'image_tag') 7 | value: ${{ jobs.gen-image-version.outputs.imagetag }} 8 | jobs: 9 | gen-image-version: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | image_tag: "${{ steps.get_tag.outputs.tag }}" 13 | steps: 14 | - run: ./output_image_tag.sh 15 | id: get_tag 16 | -------------------------------------------------------------------------------- /testdata/examples/runner_label_check.out: -------------------------------------------------------------------------------- 1 | /test\.yaml:10:13: label "linux-latest" is unknown\. available labels are .+\. if it is a custom label for self-hosted runner, set list of labels in actionlint\.yaml config file \[runner-label\]/ 2 | /test\.yaml:16:13: label "gpu" is unknown\. available labels are .+\. if it is a custom label for self-hosted runner, set list of labels in actionlint\.yaml config file \[runner-label\]/ 3 | /test\.yaml:23:14: label "macos-10.13" is unknown\. available labels are .+\. if it is a custom label for self-hosted runner, set list of labels in actionlint\.yaml config file \[runner-label\]/ 4 | -------------------------------------------------------------------------------- /testdata/examples/runner_label_conflict.out: -------------------------------------------------------------------------------- 1 | test.yaml:4:30: label "windows-latest" conflicts with label "ubuntu-latest" defined at line:4,col:15. note: to run your job on each workers, use matrix [runner-label] 2 | -------------------------------------------------------------------------------- /testdata/examples/runner_label_conflict.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: [ubuntu-latest, windows-latest] 5 | steps: 6 | - run: echo ... 7 | -------------------------------------------------------------------------------- /testdata/examples/shell_name_validation.out: -------------------------------------------------------------------------------- 1 | test.yaml:8:16: shell name "dash" is invalid. available names are "bash", "pwsh", "python", "sh" [shell-name] 2 | test.yaml:11:16: shell name "powershell" is invalid on macOS or Linux. available names are "bash", "pwsh", "python", "sh" [shell-name] 3 | test.yaml:17:16: shell name "fish" is invalid. available names are "bash", "pwsh", "python", "sh" [shell-name] 4 | test.yaml:27:16: shell name "sh" is invalid on Windows. available names are "bash", "cmd", "powershell", "pwsh", "python" [shell-name] 5 | -------------------------------------------------------------------------------- /testdata/examples/shellcheck_integration.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:9: shellcheck reported issue in this script: SC2086:info:1:6: Double quote to prevent globbing and word splitting [shellcheck] 2 | test.yaml:14:9: shellcheck reported issue in this script: SC2086:info:1:6: Double quote to prevent globbing and word splitting [shellcheck] 3 | -------------------------------------------------------------------------------- /testdata/examples/shellcheck_integration.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo $FOO 7 | test-win: 8 | runs-on: windows-latest 9 | steps: 10 | # Shell on Windows is PowerShell by default. 11 | # shellcheck is not run in this case. 12 | - run: echo $FOO 13 | # This script is run with bash due to 'shell:' configuration 14 | - run: echo $FOO 15 | shell: bash 16 | -------------------------------------------------------------------------------- /testdata/examples/type_checks.out: -------------------------------------------------------------------------------- 1 | test.yaml:7:28: property access of object must be type of string but got "number" [expression] 2 | test.yaml:9:24: property "os" is not defined in object type {id: string; network: string} [expression] 3 | test.yaml:11:24: receiver of object dereference "owner" must be type of object but got "string" [expression] 4 | test.yaml:13:20: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type {string => string} [expression] 5 | -------------------------------------------------------------------------------- /testdata/examples/type_checks.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # ERROR: `env` is object. Index access to object is invalid 7 | - run: echo '${{ env[0] }}' 8 | # ERROR: Properties in objects are strongly typed. Missing property can be caught 9 | - run: echo '${{ job.container.os }}' 10 | # ERROR: `github.repository` is string. Trying to access .owner property is invalid 11 | - run: echo '${{ github.repository.owner }}' 12 | # ERROR: Objects, arrays and null should not be evaluated at ${{ }} since the outputs are useless 13 | - run: echo '${{ env }}' 14 | -------------------------------------------------------------------------------- /testdata/examples/unexpected_keys.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:5: unexpected key "default" for "job" section. expected one of "concurrency", "container", "continue-on-error", "defaults", "env", "environment", "if", "name", "needs", "outputs", "permissions", "runs-on", "secrets", "services", "steps", "strategy", "timeout-minutes", "uses", "with" [syntax-check] 2 | test.yaml:12:9: unexpected key "Shell" for "step" section. expected one of "continue-on-error", "env", "id", "if", "name", "run", "shell", "timeout-minutes", "uses", "with", "working-directory" [syntax-check] 3 | -------------------------------------------------------------------------------- /testdata/examples/unexpected_keys.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | # ERROR: Typo of `defaults:` 6 | default: 7 | run: 8 | working-directory: /path/to/dir 9 | steps: 10 | - run: echo hello 11 | # ERROR: `shell:` must be in lower case 12 | Shell: bash 13 | -------------------------------------------------------------------------------- /testdata/examples/unexpected_mapping_values.out: -------------------------------------------------------------------------------- 1 | test.yaml:6:18: expecting a single ${{...}} expression or boolean literal "true" or "false", but found plain text node [syntax-check] 2 | test.yaml:8:21: expected scalar node for integer value but found scalar node with "!!float" tag [syntax-check] 3 | test.yaml:13:26: expecting a single ${{...}} expression or float number literal, but found plain text node [syntax-check] 4 | -------------------------------------------------------------------------------- /testdata/examples/unexpected_mapping_values.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | # ERROR: Boolean value "true" or "false" is expected 6 | fail-fast: off 7 | # ERROR: Integer value is expected 8 | max-parallel: 1.5 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: sleep 200 12 | # ERROR: Float value is expected 13 | timeout-minutes: two minutes 14 | -------------------------------------------------------------------------------- /testdata/examples/webhook_checks.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | # ERROR: Incorrect filter. 'branches' is correct 4 | branch: foo 5 | # ERROR: Both 'paths' and 'paths-ignore' filters cannot be used for the same event 6 | paths: path/to/foo 7 | paths-ignore: path/to/foo 8 | issues: 9 | # ERROR: Incorrect type. 'opened' is correct 10 | types: created 11 | release: 12 | # ERROR: 'tags' filter is not available for 'release' event 13 | tags: v*.*.* 14 | # ERROR: Unknown event name 15 | pullreq: 16 | 17 | jobs: 18 | test: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - run: echo ... 22 | -------------------------------------------------------------------------------- /testdata/examples/workflow_call_definitions.out: -------------------------------------------------------------------------------- 1 | test.yaml:15:18: input of workflow_call event "port" is typed as number but its default value ":1234" cannot be parsed as a float number: strconv.ParseFloat: parsing ":1234": invalid syntax [events] 2 | test.yaml:20:15: invalid value "object" for input type of workflow_call event. it must be one of "boolean", "number", or "string" [syntax-check] 3 | test.yaml:25:18: input "path" of workflow_call event has the default value "", but it is also required. if an input is marked as required, its default value will never be used [events] 4 | -------------------------------------------------------------------------------- /testdata/examples/workflow_call_jobs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | job1: 4 | uses: owner/repo/path/to/workflow.yml@v1 5 | # ERROR: 'runs-on' is not available on calling reusable workflow 6 | runs-on: ubuntu-latest 7 | job2: 8 | # ERROR: Local file path with ref is not available 9 | uses: ./.github/workflows/ci.yml@main 10 | job3: 11 | # ERROR: 'with' is only available on calling reusable workflow 12 | with: 13 | foo: bar 14 | runs-on: ubuntu-latest 15 | steps: 16 | - run: echo hello 17 | job4: 18 | # ERROR: This workflow does not exist 19 | uses: ./.github/workflows/not-existing.yml 20 | -------------------------------------------------------------------------------- /testdata/examples/workflow_inputs_secrets_types.out: -------------------------------------------------------------------------------- 1 | test.yaml:20:23: property "uri" is not defined in object type {lucky_number: number; url: string} [expression] 2 | test.yaml:23:22: property "credentials" is not defined in object type {actions_runner_debug: string; actions_step_debug: string; credential: string; github_token: string} [expression] 3 | -------------------------------------------------------------------------------- /testdata/examples/workflow_inputs_secrets_types.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | url: 5 | description: 'your URL' 6 | type: string 7 | lucky_number: 8 | description: 'your lucky number' 9 | type: number 10 | secrets: 11 | credential: 12 | description: 'your credential' 13 | 14 | jobs: 15 | test: 16 | runs-on: ubuntu-20.04 17 | steps: 18 | - name: Send data 19 | # ERROR: uri is typo of url 20 | run: curl ${{ inputs.uri }} -d ${{ inputs.lucky_number }} 21 | env: 22 | # ERROR: credentials is typo of credential 23 | TOKEN: ${{ secrets.credentials }} 24 | -------------------------------------------------------------------------------- /testdata/find_project/.github/reusable/broken.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | outputs: this causes an error 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - run: echo ... 10 | -------------------------------------------------------------------------------- /testdata/find_project/.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller: 5 | uses: ./.github/reusable/broken.yaml 6 | -------------------------------------------------------------------------------- /testdata/find_project/README.md: -------------------------------------------------------------------------------- 1 | This directory is used for testing that actionlint can detect a repository root. 2 | 3 | - `TestLintFindProjectFromPath` in `linter_test.go` 4 | - `project_test.go` 5 | 6 | `.git` directory is dynamically created when the test case is run because Git doesn't allow committing `.git` directory. 7 | -------------------------------------------------------------------------------- /testdata/format/test.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branch: main 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - run: echo ${{ matrix.msg }} 10 | with: 11 | arg: foo 12 | -------------------------------------------------------------------------------- /testdata/ok/allow_any_outputs.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: dorny/paths-filter@v3 8 | id: filter 9 | with: 10 | filters: ... 11 | - run: npm run lint:md 12 | if: ${{ steps.filter.outputs.md == 'true' }} 13 | -------------------------------------------------------------------------------- /testdata/ok/bool_conversion.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo '${{ !!42 }}' 7 | if: github.event_name 8 | -------------------------------------------------------------------------------- /testdata/ok/dynamic_shell_name.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | defaults: 4 | run: 5 | shell: ${{ 'sh {0}' }} 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | defaults: 11 | run: 12 | shell: ${{ 'sh {0}' }} 13 | steps: 14 | - run: echo hi 15 | shell: ${{ 'sh {0}' }} 16 | -------------------------------------------------------------------------------- /testdata/ok/filters_for_specific_events.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | merge_group: 3 | branches: main 4 | push: 5 | branches: main 6 | tags: v*.*.* 7 | paths: path/to/foo 8 | pull_request: 9 | branches: main 10 | paths: path/to/foo 11 | pull_request_target: 12 | branches: main 13 | paths-ignore: path/to/foo 14 | workflow_run: 15 | workflows: foo.yaml 16 | branches: main 17 | 18 | jobs: 19 | test: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - run: echo ... 23 | -------------------------------------------------------------------------------- /testdata/ok/filters_ignore_for_specific_events.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | merge_group: 3 | branches-ignore: bar 4 | push: 5 | branches-ignore: test 6 | tags-ignore: deploy/* 7 | paths-ignore: path/to/foo 8 | pull_request: 9 | branches-ignore: test 10 | paths-ignore: path/to/foo 11 | pull_request_target: 12 | branches-ignore: test 13 | paths-ignore: path/to/foo 14 | workflow_run: 15 | workflows: foo.yaml 16 | branches-ignore: test 17 | 18 | jobs: 19 | test: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - run: echo ... 23 | -------------------------------------------------------------------------------- /testdata/ok/issue-101.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo ${{ runner.arch }} 8 | -------------------------------------------------------------------------------- /testdata/ok/issue-104.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - id: test 8 | uses: actions/github-script@v7 9 | with: 10 | script: | 11 | core.setOutput('foo', 'bar'); 12 | - id: call 13 | run: | 14 | echo "Test output: ${{ steps.test.outputs.foo }}" 15 | -------------------------------------------------------------------------------- /testdata/ok/issue-113.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: | 8 | if [[ -z ${{ env.FOO }} ]]; then 9 | echo "FOO is empty" 10 | fi 11 | -------------------------------------------------------------------------------- /testdata/ok/issue-145.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | services: 5 | required: true 6 | type: string 7 | description: contents of services.json 8 | 9 | jobs: 10 | build-lint-test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | service: ${{ fromJSON(inputs.services) }} 15 | exclude: 16 | # `service` is loaded from JSON. Check should be skipped 17 | - service: 18 | npm_project: foo 19 | steps: 20 | - run: echo 1 21 | -------------------------------------------------------------------------------- /testdata/ok/issue-151-child-of-child-job.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: push 4 | 5 | jobs: 6 | first: 7 | runs-on: ubuntu-latest 8 | outputs: 9 | first: 'output from parent' 10 | steps: 11 | - run: echo 'first' 12 | 13 | second: 14 | needs: [first] 15 | runs-on: ubuntu-latest 16 | outputs: 17 | second: 'output from second' 18 | steps: 19 | - run: echo 'second' 20 | 21 | third: 22 | needs: [first, second] 23 | runs-on: ubuntu-latest 24 | steps: 25 | - run: echo '${{ toJSON(needs.second.outputs) }}' 26 | - run: echo '${{ toJSON(needs.first.outputs) }}' 27 | -------------------------------------------------------------------------------- /testdata/ok/issue-152.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | my_input: 5 | type: string 6 | required: true 7 | 8 | jobs: 9 | my_job: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo ${{ github.event.inputs.my_input }} 13 | - run: echo ${{ inputs.my_input }} 14 | -------------------------------------------------------------------------------- /testdata/ok/issue-164.yaml: -------------------------------------------------------------------------------- 1 | # https://github.com/rhysd/actionlint/issues/164 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | include: 10 | - msg: hello macos 11 | host-labels: ["self-hosted", "macOS", "X64"] 12 | - msg: hello linux 13 | host-labels: ["self-hosted", "linux"] 14 | runs-on: ${{ matrix.host-labels }} 15 | steps: 16 | - run: echo "${{ matrix.msg }}" 17 | -------------------------------------------------------------------------------- /testdata/ok/issue-174-secret-description-is-optional.yaml: -------------------------------------------------------------------------------- 1 | # https://github.com/rhysd/actionlint/issues/174 2 | 3 | on: 4 | workflow_call: 5 | secrets: 6 | MY_TOKEN: 7 | 8 | jobs: 9 | main: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - run: echo hello 13 | env: 14 | GH_TOKEN: ${{ secrets.MY_TOKEN }} 15 | -------------------------------------------------------------------------------- /testdata/ok/issue-205-closing-braces-in-string.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: ${{ fromJSON('{"foo":{}}') }} 8 | 9 | steps: 10 | - run: echo 'hello' 11 | -------------------------------------------------------------------------------- /testdata/ok/issue-223.yaml: -------------------------------------------------------------------------------- 1 | run-name: "${{ inputs.property_name }}" 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | property_name: 7 | description: test 8 | required: true 9 | type: string 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - run: echo '${{ inputs.property_name }}' 16 | -------------------------------------------------------------------------------- /testdata/ok/issue-235-expr-in-input-default-value.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | # Default value of input may contain ${{ }} (#235) 4 | inputs: 5 | input1: 6 | type: string 7 | default: ${{ github.event.repository.name }} 8 | input2: 9 | type: boolean 10 | default: ${{ github.event.name != 'pull_request' }} 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - run: echo '${{ inputs.input1 }}' 17 | - run: echo '${{ inputs.input2 }}' 18 | -------------------------------------------------------------------------------- /testdata/ok/issue-261_matrix_row_item_constructed_with_expr.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | custom-build-type: 5 | type: string 6 | 7 | jobs: 8 | build_matrix: 9 | strategy: 10 | matrix: 11 | build-type: 12 | - debug 13 | - ${{ fromJson(inputs.custom-build-type) }} 14 | exclude: 15 | # 'release' is not listed in the matrix row, but it should not be reported as array 16 | # since one of the element of the row array is dynamically constructed with ${{ }} (#261) 17 | - build-type: release 18 | runs-on: ubuntu-latest 19 | steps: 20 | - run: echo ${{ matrix.build-type }} 21 | -------------------------------------------------------------------------------- /testdata/ok/issue-280_runs_on.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: push 3 | 4 | jobs: 5 | test1: 6 | runs-on: 7 | group: ubuntu-runners 8 | steps: 9 | - uses: actions/checkout@v4 10 | test2: 11 | runs-on: 12 | labels: x64 13 | steps: 14 | - uses: actions/checkout@v4 15 | test3: 16 | runs-on: 17 | group: ubuntu-runners 18 | labels: x64 19 | steps: 20 | - uses: actions/checkout@v4 21 | test4: 22 | runs-on: 23 | labels: [x64, self-hosted] 24 | steps: 25 | - uses: actions/checkout@v4 26 | test5: 27 | runs-on: 28 | group: ubuntu-runners 29 | labels: [x64, self-hosted] 30 | steps: 31 | - uses: actions/checkout@v4 32 | -------------------------------------------------------------------------------- /testdata/ok/issue-285_expr_assign_at_matrix_row.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | test: 9 | - ${{ fromJson('{"foo":{"bar":"test"}}') }} 10 | steps: 11 | - run: echo ${{ matrix.test.foo.bar }} 12 | -------------------------------------------------------------------------------- /testdata/ok/issue-294_matrix_row_array_expression.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | numbers: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | PR: ${{ github.event.workflow_run.pull_requests.*.number }} 9 | steps: 10 | - run: echo '${{ matrix.PR }}' 11 | -------------------------------------------------------------------------------- /testdata/ok/issue-30.yaml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | myjob: 5 | environment: 6 | name: env-name 7 | url: ${{ steps.thing.outputs.app-url }} 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: 'Run Azure Functions Action' 11 | uses: Azure/functions-action@v1.4.0 12 | id: thing 13 | with: 14 | app-name: 'my-function-app' 15 | package: my.zip 16 | -------------------------------------------------------------------------------- /testdata/ok/issue-31.yaml: -------------------------------------------------------------------------------- 1 | name: Create new release 2 | on: 3 | workflow_dispatch: 4 | 5 | jobs: 6 | create_release: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: ncipollo/release-action@v1 11 | with: 12 | allowUpdates: false 13 | -------------------------------------------------------------------------------- /testdata/ok/issue-355-shellcheck-sc2043.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: | 7 | for x in ${{ github.run_number }}; do 8 | echo "$x" 9 | done 10 | -------------------------------------------------------------------------------- /testdata/ok/issue-384_narrow-logical-op.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | and_op_narrowing: 5 | runs-on: ubuntu-latest 6 | # `timeout-minutes` only allow number type value 7 | timeout-minutes: ${{ '...' && 60 || 20 }} 8 | steps: 9 | - run: echo hello 10 | # `timeout-minutes` only allow number type value 11 | timeout-minutes: ${{ '...' && 60 || 20 }} 12 | or_op_narrowing: 13 | runs-on: ubuntu-latest 14 | # `timeout-minutes` only allow number type value 15 | timeout-minutes: ${{ ('...' || 60) && 20 }} 16 | steps: 17 | - run: echo hello 18 | # `timeout-minutes` only allow number type value 19 | timeout-minutes: ${{ ('...' || 60) && 20 }} 20 | -------------------------------------------------------------------------------- /testdata/ok/issue-402-expression-in-services.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | tests: 4 | runs-on: ubuntu-latest 5 | services: ${{ fromJSON('{}') }} 6 | steps: 7 | - run: echo 8 | -------------------------------------------------------------------------------- /testdata/ok/issue-409-shellcheck-default-shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | defaults: 4 | run: 5 | shell: pwsh 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | # This script should not be checked by shellcheck 12 | - run: $Env:FOO = "FOO" 13 | -------------------------------------------------------------------------------- /testdata/ok/issue-412_runner_environment_prop.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | job-level: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 'self-hosted runner' 8 | if: ${{ runner.environment == 'self-hosted' }} 9 | -------------------------------------------------------------------------------- /testdata/ok/issue-414_expression_in_matrix_exclude.yaml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | foo: ['a', 'b'] 9 | exclude: 10 | - foo: ${{ github.event_name }} 11 | steps: 12 | - run: echo ${{ matrix.foo }} 13 | -------------------------------------------------------------------------------- /testdata/ok/issue-442-download-artifact-v3-workaround.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/download-artifact@v3-node20 8 | with: 9 | name: my-artifact 10 | id: dl 11 | # This output is missing in action.yml of actions/download-artifact@v3-node20 (#442). 12 | # We added the workaround to add the output in scripts/generate-popular-actions/ script to avoid the issue. 13 | - run: echo ${{ steps.dl.outputs.download-path }} 14 | -------------------------------------------------------------------------------- /testdata/ok/issue-45.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: | 7 | if [[ "${{ github.event.sender.login}}" == "rhysd" ]]; then 8 | echo "it's me" 9 | fi 10 | -------------------------------------------------------------------------------- /testdata/ok/issue-495_with_args.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | test: 5 | type: string 6 | 7 | jobs: 8 | my_job: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: docker://example.com:latest 12 | with: 13 | entrypoint: "${{ inputs.test }}" 14 | args: --arg "${{ inputs.test }}" 15 | -------------------------------------------------------------------------------- /testdata/ok/issue-53-shellcheck-sc2154.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo hello "$thing" 7 | env: 8 | thing: world 9 | -------------------------------------------------------------------------------- /testdata/ok/issue-67.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | # https://github.com/rhysd/actionlint/pull/67 7 | - run: echo '${{ runner.os }}' 8 | - run: echo '${{ runner.name }}' 9 | -------------------------------------------------------------------------------- /testdata/ok/issue-87.yaml: -------------------------------------------------------------------------------- 1 | name: Lint Code Base 2 | 3 | on: 4 | push: 5 | workflow_call: 6 | 7 | jobs: 8 | build: 9 | name: Lint Code Base 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout Code 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Lint Code Base 19 | uses: github/super-linter@v4 20 | env: 21 | VALIDATE_ALL_CODEBASE: false 22 | DEFAULT_BRANCH: master 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /testdata/ok/matrix_in_array_at_runs_on.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | build: 5 | strategy: 6 | matrix: 7 | host: ["macOS", "linux"] 8 | runs-on: ["self-hosted", "${{ matrix.host }}"] 9 | steps: 10 | - run: echo "hello, ${{ matrix.host }}" 11 | -------------------------------------------------------------------------------- /testdata/ok/minimal.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo 7 | -------------------------------------------------------------------------------- /testdata/ok/multi_labels_no_conflict.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | os: [ubuntu-22.04, linux] 7 | runs-on: [ubuntu-latest, '${{matrix.os}}'] 8 | steps: 9 | - run: echo 10 | -------------------------------------------------------------------------------- /testdata/ok/nested_workflow_call.yaml: -------------------------------------------------------------------------------- 1 | on: workflow_call 2 | 3 | jobs: 4 | test: 5 | uses: owner/repo/x.yml@ref 6 | -------------------------------------------------------------------------------- /testdata/ok/no_description_workflow_call.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | username: 5 | required: true 6 | type: string 7 | secrets: 8 | token: 9 | required: true 10 | outputs: 11 | firstword: 12 | value: ${{ jobs.example_job.outputs.output1 }} 13 | 14 | jobs: 15 | example_job: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | output1: ${{ steps.step1.outputs.firstword }} 19 | steps: 20 | - id: step1 21 | run: echo "firstword=hello, ${{ inputs.username }}" >> "$GITHUB_OUTPUT" 22 | env: 23 | TOKEN: ${{ secrets.token }} 24 | -------------------------------------------------------------------------------- /testdata/ok/no_local_action_found.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - uses: ./.github/action-does-not-exist 7 | with: 8 | foo: aaa 9 | bar: bbb 10 | -------------------------------------------------------------------------------- /testdata/ok/operator_outside_expr.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | ver: [0, 1] 7 | runs-on: ubuntu-latest 8 | steps: 9 | - run: echo 'my repo' 10 | # This was not ok until https://github.com/github/docs/pull/8786 11 | if: contains(github.repository, 'rhysd') 12 | - run: echo 'my repo' 13 | # This was not ok until https://github.com/github/docs/pull/8786 14 | if: github.repository != 'rhysd/foo' && matrix.ver > 0 || !(github.token == '') 15 | -------------------------------------------------------------------------------- /testdata/ok/outputs_in_environment_url.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | environment: 7 | name: staging 8 | url: https://example.com/${{ steps.test.outputs.path }} 9 | steps: 10 | - run: echo "test=hello" >> "$GITHUB_OUTPUT" 11 | id: test 12 | -------------------------------------------------------------------------------- /testdata/ok/pyflakes_python_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | defaults: 3 | run: 4 | shell: python 5 | jobs: 6 | step-level: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - run: print('hello') 10 | shell: python 11 | - run: print('hello') 12 | shell: 'python {0}' 13 | job-level: 14 | runs-on: ubuntu-latest 15 | defaults: 16 | run: 17 | shell: python 18 | steps: 19 | - run: print('hello') 20 | workflow-level: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - run: print('hello') 24 | -------------------------------------------------------------------------------- /testdata/ok/reusable_workflow_inherit_secrets.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | 4 | jobs: 5 | pass-secret-to-action: 6 | runs-on: ubuntu-latest 7 | steps: 8 | # The CALLING_WORKFLOW_SECRET secret is passed with `secrets: inherit` 9 | - name: Use a repo or org secret from the calling workflow. 10 | run: echo ${{ secrets.CALLING_WORKFLOW_SECRET }} 11 | -------------------------------------------------------------------------------- /testdata/ok/run_name.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#run-name 2 | # https://github.blog/changelog/2022-09-26-github-actions-dynamic-names-for-workflow-runs/ 3 | run-name: Deploy by @${{ github.actor }} 4 | 5 | on: push 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - run: echo hello 12 | -------------------------------------------------------------------------------- /testdata/ok/shell_name_case_insensitive.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: push 3 | 4 | jobs: 5 | test: 6 | runs-on: windows-latest 7 | steps: 8 | - run: echo hi 9 | shell: powershell 10 | - run: echo hi 11 | shell: PowerShell 12 | - run: echo hi 13 | shell: POWERSHELL 14 | -------------------------------------------------------------------------------- /testdata/ok/shellcheck_detect_shell_from_runner.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | runs-on: windows-latest 5 | steps: 6 | # This script should not be checked by shellcheck 7 | - run: $Env:FOO = "FOO" 8 | -------------------------------------------------------------------------------- /testdata/ok/shellcheck_job_default_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | defaults: 3 | run: 4 | shell: bash 5 | jobs: 6 | test: 7 | defaults: 8 | run: 9 | shell: pwsh 10 | runs-on: ubuntu-latest 11 | steps: 12 | # This script should not be checked by shellcheck 13 | - run: $Env:FOO = "FOO" 14 | -------------------------------------------------------------------------------- /testdata/ok/shellcheck_step_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | test: 4 | strategy: 5 | matrix: 6 | os: [ubuntu-latest, windows-latest] 7 | runs-on: ${{ matrix.os }} 8 | steps: 9 | # This causes SC1001 if `shell:` is not set explicitly 10 | - run: cat xxx\yyy.txt 11 | if: ${{ matrix.os == 'windows-latest' }} 12 | shell: pwsh 13 | # This causes SC1001 if `shell:` is not set explicitly 14 | - run: cat xxx\yyy.txt 15 | if: ${{ matrix.os == 'windows-latest' }} 16 | shell: 'pwsh {0}' 17 | -------------------------------------------------------------------------------- /testdata/ok/shellcheck_workflow_default_shell.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | defaults: 3 | run: 4 | shell: pwsh 5 | jobs: 6 | test1: 7 | runs-on: ubuntu-latest 8 | steps: 9 | # This script should not be checked by shellcheck 10 | - run: $Env:FOO = "FOO" 11 | test2: 12 | runs-on: ubuntu-latest 13 | steps: 14 | # This script should not be checked by shellcheck 15 | - run: $Env:FOO = "FOO" 16 | -------------------------------------------------------------------------------- /testdata/ok/skip_inputs.yaml: -------------------------------------------------------------------------------- 1 | name: Log latest release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | logLatestRelease: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: octokit/request-action@v2.x 12 | id: get_latest_release 13 | with: 14 | route: GET /repos/{owner}/{repo}/releases/latest 15 | owner: octokit 16 | repo: request-action 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | - run: "echo latest release: ${{ steps.get_latest_release.outputs.data }}" 20 | -------------------------------------------------------------------------------- /testdata/ok/untrusted_inputs.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: push 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | # Direct access to untrusted inputs 9 | - run: | 10 | echo "ISSUE: $ISSUE, COMMITS: $COMMIT, REF: $REF" 11 | env: 12 | ISSUE: ${{ github.event.issue.body }} 13 | COMMITS: ${{ toJSON(github.event.commits.*.message) }} 14 | REF: ${{ github.head_ref }} 15 | # Indirect access to untrusted inputs via object filter 16 | - run: | 17 | echo "BODIES: $BODIES, EMAILS: $EMAILS" 18 | env: 19 | BODIES: ${{ toJSON(github.event.*.body) }} 20 | EMAILS: ${{ toJSON(github.event.*.*.email) }} 21 | -------------------------------------------------------------------------------- /testdata/ok/using_reusable_workflow_call_outputs.yaml: -------------------------------------------------------------------------------- 1 | name: Call a reusable workflow and use its outputs 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | job1: 8 | uses: octo-org/example-repo/.github/workflows/called-workflow.yml@v1 9 | 10 | job2: 11 | runs-on: ubuntu-latest 12 | needs: job1 13 | steps: 14 | - run: echo ${{ needs.job1.outputs.firstword }} ${{ needs.job1.outputs.secondword }} 15 | -------------------------------------------------------------------------------- /testdata/ok/workflow_call_job.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | jobs: 3 | call1: 4 | uses: org/repo/workflow.yml@v1 5 | call2: 6 | uses: org/repo/workflow.yml@v1 7 | with: 8 | foo: bar 9 | call3: 10 | uses: org/repo/workflow.yml@v1 11 | secrets: 12 | foo: bar 13 | call4: 14 | uses: org/repo/workflow.yml@v1 15 | with: 16 | foo: bar 17 | secrets: 18 | foo: bar 19 | call5: 20 | name: Test 21 | uses: org/repo/workflow.yml@v1 22 | with: 23 | foo: bar 24 | secrets: 25 | foo: bar 26 | needs: ['call1'] 27 | permissions: read-all 28 | call6: 29 | # Edge case. Give up checking format. 30 | uses: ${{ 'oooops' }} 31 | -------------------------------------------------------------------------------- /testdata/ok/workflow_call_outputs.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | image-name: 5 | description: Name of Docker image 6 | required: true 7 | type: string 8 | outputs: 9 | image-version: 10 | description: "Docker image version" 11 | value: ${{ jobs.generate-image-version.outputs.imagetag }} 12 | jobs: 13 | generate-image-version: 14 | runs-on: ubuntu-latest 15 | outputs: 16 | imagetag: "${{ steps.hello.outputs.foo }}" 17 | steps: 18 | - run: echo hello 19 | id: hello 20 | -------------------------------------------------------------------------------- /testdata/ok/workflow_call_with_strategy.yaml: -------------------------------------------------------------------------------- 1 | name: Reusable workflow with matrix strategy 2 | 3 | # This was enabled by https://github.blog/changelog/2022-08-22-github-actions-improvements-to-reusable-workflows-2/ 4 | # 5 | # Issue: #197 6 | # Doc: https://docs.github.com/en/actions/using-workflows/reusing-workflows#example-matrix-strategy-with-a-reusable-workflow 7 | 8 | on: 9 | push: 10 | 11 | jobs: 12 | ReusableMatrixJobForDeployment: 13 | strategy: 14 | matrix: 15 | target: [dev, stage, prod] 16 | uses: octocat/octo-repo/.github/workflows/deployment.yml@main 17 | with: 18 | target: ${{ matrix.target }} 19 | -------------------------------------------------------------------------------- /testdata/ok/workflow_dispatch_upper_case_inputs.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | logLevel: 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - run: echo '${{ inputs.logLevel }}' 11 | - run: echo '${{ inputs.loglevel }}' 12 | - run: echo '${{ inputs.LOGLEVEL }}' 13 | -------------------------------------------------------------------------------- /testdata/projects/broken_local_action.out: -------------------------------------------------------------------------------- 1 | /workflows/test\.yaml:7:15: could not parse action metadata in ".+broken_input": yaml: .+ \[action\]/ 2 | /workflows/test\.yaml:10:15: could not parse action metadata in ".+broken_output": yaml: outputs must be mapping node but scalar node was found at line:4, col:10 \[action\]/ 3 | /workflows/test\.yaml:11:15: could not parse action metadata in ".+duplicate_input": input "FOO" is duplicated \[action\]/ 4 | /workflows/test\.yaml:14:15: could not parse action metadata in ".+duplicate_output": output "FOO" is duplicated \[action\]/ 5 | -------------------------------------------------------------------------------- /testdata/projects/broken_local_action/action/broken_input/action.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | description: hello 3 | 4 | inputs: 5 | hello: 6 | required: 7 | this: value 8 | should-be: boolean 9 | -------------------------------------------------------------------------------- /testdata/projects/broken_local_action/action/broken_output/action.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | description: hello 3 | 4 | outputs: oops 5 | -------------------------------------------------------------------------------- /testdata/projects/broken_local_action/action/duplicate_input/action.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | description: hello 3 | 4 | inputs: 5 | foo: 6 | description: ... 7 | FOO: 8 | description: ... 9 | -------------------------------------------------------------------------------- /testdata/projects/broken_local_action/action/duplicate_output/action.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | description: hello 3 | 4 | outputs: 5 | foo: 6 | description: ... 7 | FOO: 8 | description: ... 9 | -------------------------------------------------------------------------------- /testdata/projects/broken_local_action/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./action/broken_input 8 | with: 9 | hello: world 10 | - uses: ./action/broken_output 11 | - uses: ./action/duplicate_input 12 | with: 13 | foo: ... 14 | - uses: ./action/duplicate_output 15 | # Check: Do not repeat error 16 | - uses: ./action/broken_output 17 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow.out: -------------------------------------------------------------------------------- 1 | /workflows/test\.yaml:5:11: error while parsing reusable workflow "\./reusable/broken\.yaml": yaml: .+ \[workflow-call\]/ 2 | workflows/test.yaml:7:11: error while parsing reusable workflow "./reusable/no_hook.yaml": "workflow_call" event trigger is not found in "on:" at line:1, column:5 [workflow-call] 3 | workflows/test.yaml:9:11: error while parsing reusable workflow "./reusable/no_on.yaml": "on:" is not found [workflow-call] 4 | /workflows/test\.yaml:15:11: error while parsing reusable workflow "\./reusable/broken_secrets\.yaml": yaml: .+ \[workflow-call\]/ 5 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/README.md: -------------------------------------------------------------------------------- 1 | Reusable workflows are separate in [`reusable`](./reusable) otherwise errors are not deterministic. When some broken workflow is parsed first, it causes parse error `ReusableWorkflowMetadata` is created from `WorkflowCallEvent` AST node. But when `test.yaml` is parsed first, `ReusableWorkflowMetadata` instance is parsed in `reusable_workflow.go` and causes its own parse error. 2 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/reusable/broken.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | outputs: my cool outputs 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - run: echo ... 10 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/reusable/broken_input.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | empty_input: 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - run: echo ... 11 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/reusable/broken_secrets.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | secrets: my secrets... 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - run: echo ... 10 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/reusable/no_hook.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo ... 8 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/reusable/no_on.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | test: 3 | runs-on: ubuntu-latest 4 | steps: 5 | - run: echo ... 6 | -------------------------------------------------------------------------------- /testdata/projects/broken_reusable_workflow/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller1: 5 | uses: ./reusable/broken.yaml 6 | caller2: 7 | uses: ./reusable/no_hook.yaml 8 | caller3: 9 | uses: ./reusable/no_on.yaml 10 | caller4: 11 | uses: ./reusable/broken_input.yaml 12 | with: 13 | empty_input: 14 | caller5: 15 | uses: ./reusable/broken_secrets.yaml 16 | -------------------------------------------------------------------------------- /testdata/projects/config_variables.out: -------------------------------------------------------------------------------- 1 | workflows/test.yaml:14:24: undefined configuration variable "piyo". defined configuration variables in actionlint.yaml are "FOO", "WOO" [expression] 2 | -------------------------------------------------------------------------------- /testdata/projects/config_variables/actionlint.yaml: -------------------------------------------------------------------------------- 1 | config-variables: [WOO, FOO] 2 | -------------------------------------------------------------------------------- /testdata/projects/config_variables/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | # These are listed 8 | - run: echo '${{ vars.FOO }}' 9 | - run: echo '${{ vars.WOO }}' 10 | # Name is case-insensitive 11 | - run: echo '${{ vars.foo }}' 12 | - run: echo '${{ vars.Foo }}' 13 | # ERROR: Undefined var 14 | - run: echo '${{ vars.PIYO }}' 15 | -------------------------------------------------------------------------------- /testdata/projects/example_inputs_secrets_in_workflow_call/.github/workflows/reusable.yaml: -------------------------------------------------------------------------------- 1 | # .github/workflows/reusable.yaml 2 | on: 3 | workflow_call: 4 | inputs: 5 | name: 6 | type: string 7 | required: true 8 | id: 9 | type: number 10 | message: 11 | type: string 12 | secrets: 13 | password: 14 | required: true 15 | 16 | jobs: 17 | test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - run: echo '${{ outputs.required_input }}' 21 | -------------------------------------------------------------------------------- /testdata/projects/example_workflow_call_outputs_downstream_jobs.out: -------------------------------------------------------------------------------- 1 | workflows/test.yaml:13:24: property "tag" is not defined in object type {version: string} [expression] 2 | -------------------------------------------------------------------------------- /testdata/projects/example_workflow_call_outputs_downstream_jobs/.github/workflows/get-build-info.yaml: -------------------------------------------------------------------------------- 1 | # .github/workflows/get-build-info.yaml 2 | on: 3 | workflow_call: 4 | outputs: 5 | version: 6 | value: ${{ outputs.version }} 7 | description: version of software 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | outputs: 13 | version: ${{ steps.get_version.outputs.version }} 14 | steps: 15 | - run: ... 16 | id: get_version 17 | -------------------------------------------------------------------------------- /testdata/projects/example_workflow_call_outputs_downstream_jobs/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | get_build_info: 5 | uses: ./.github/workflows/get-build-info.yaml 6 | downstream: 7 | needs: [get_build_info] 8 | runs-on: ubuntu-latest 9 | steps: 10 | # OK. `version` is defined in the reusable workflow 11 | - run: echo '${{ needs.get_build_info.outputs.version }}' 12 | # ERROR: `tag` is not defined in the reusable workflow 13 | - run: echo '${{ needs.get_build_info.outputs.tag }}' 14 | -------------------------------------------------------------------------------- /testdata/projects/issue-136.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/issue-136.out -------------------------------------------------------------------------------- /testdata/projects/issue-136/workflows/reusable.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo hello 9 | -------------------------------------------------------------------------------- /testdata/projects/issue-136/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | checks: 5 | concurrency: 6 | group: some-group 7 | cancel-in-progress: true 8 | uses: ./workflows/reusable.yaml 9 | -------------------------------------------------------------------------------- /testdata/projects/issue173.out: -------------------------------------------------------------------------------- 1 | workflows/workflow1.yaml:9:15: missing input "hello" which is required by action "My action" defined at "./action". all required inputs are "hello" [action] 2 | workflows/workflow1.yaml:11:11: input "goodbye" is not defined in action "My action" defined at "./action". available inputs are "hello" [action] 3 | workflows/workflow2.yaml:9:15: missing input "hello" which is required by action "My action" defined at "./action". all required inputs are "hello" [action] 4 | workflows/workflow2.yaml:11:11: input "goodbye" is not defined in action "My action" defined at "./action". available inputs are "hello" [action] 5 | -------------------------------------------------------------------------------- /testdata/projects/issue173/action/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | hello: 7 | description: message 8 | required: true 9 | 10 | runs: 11 | using: 'node20' 12 | main: 'index.js' 13 | -------------------------------------------------------------------------------- /testdata/projects/issue173/action/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/issue173/action/index.js -------------------------------------------------------------------------------- /testdata/projects/issue173/workflows/workflow1.yaml: -------------------------------------------------------------------------------- 1 | name: 'workflow 1' 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: ./action 10 | with: 11 | goodbye: bye 12 | -------------------------------------------------------------------------------- /testdata/projects/issue173/workflows/workflow2.yaml: -------------------------------------------------------------------------------- 1 | name: 'workflow 2' 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: ./action 10 | with: 11 | goodbye: sayonara 12 | -------------------------------------------------------------------------------- /testdata/projects/local_action_branding.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_branding.out -------------------------------------------------------------------------------- /testdata/projects/local_action_branding/action/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | branding: 6 | icon: edit 7 | color: blue 8 | 9 | runs: 10 | using: 'node20' 11 | main: 'index.js' 12 | -------------------------------------------------------------------------------- /testdata/projects/local_action_branding/action/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_branding/action/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_branding/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./action 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_case_insensitive.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_case_insensitive.out -------------------------------------------------------------------------------- /testdata/projects/local_action_case_insensitive/action/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | inputs: 6 | NAME: 7 | description: your name 8 | MESSAGE: 9 | description: message to this action 10 | 11 | outputs: 12 | USER_ID: 13 | description: user ID 14 | 15 | runs: 16 | using: 'node20' 17 | main: 'index.js' 18 | -------------------------------------------------------------------------------- /testdata/projects/local_action_case_insensitive/action/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_case_insensitive/action/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_case_insensitive/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./action 8 | with: 9 | name: rhysd 10 | message: hello 11 | id: my_action 12 | - run: echo 'User ID is ${{ steps.my_action.outputs.user_id }}' 13 | -------------------------------------------------------------------------------- /testdata/projects/local_action_empty.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_empty.out -------------------------------------------------------------------------------- /testdata/projects/local_action_empty/action/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | runs: 6 | using: 'node20' 7 | main: 'index.js' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_empty/action/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_empty/action/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_empty/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./action 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/branding/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Incorrect branding' 2 | author: 'rhysd ' 3 | description: 'my action' 4 | 5 | branding: 6 | icon: 'does-not-exist' 7 | color: 'no-color' 8 | 9 | runs: 10 | using: 'node20' 11 | main: 'index.js' 12 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/branding/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_invalid/branding/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/no_desc/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'My action' 2 | author: 'rhysd ' 3 | 4 | runs: 5 | using: 'node20' 6 | main: 'index.js' 7 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/no_desc/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_invalid/no_desc/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/no_name/action.yaml: -------------------------------------------------------------------------------- 1 | author: 'rhysd ' 2 | description: 'my action' 3 | 4 | runs: 5 | using: 'node20' 6 | main: 'index.js' 7 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/no_name/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_invalid/no_name/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./no_name 8 | - uses: ./no_desc 9 | - uses: ./branding 10 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/invalid_node_version/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Invalid node version' 2 | author: 'rhysd ' 3 | description: 'Node version must be unsigned integer in runs.using runner name' 4 | 5 | runs: 6 | using: 'nodenext' 7 | main: 'index.js' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/invalid_node_version/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_invalid_runners/invalid_node_version/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/missing_runs/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Missing runner name' 2 | author: 'rhysd ' 3 | description: 'Runner name is required' 4 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/missing_using/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'No using' 2 | author: 'rhysd ' 3 | description: '"using" is required' 4 | 5 | runs: 6 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/node12/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Node.js v12 runner' 2 | author: 'rhysd ' 3 | description: 'Runner node version is too old' 4 | 5 | runs: 6 | using: 'node12' 7 | main: 'index.js' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/node12/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_invalid_runners/node12/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/old_node/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Old Node.js' 2 | author: 'rhysd ' 3 | description: 'Runner node version is too old' 4 | 5 | runs: 6 | using: 'node16' 7 | main: 'index.js' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/old_node/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/local_action_invalid_runners/old_node/index.js -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/unknown_runner/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Unknown runner name' 2 | author: 'rhysd ' 3 | description: 'Runner name must be "docker", "composite", "node..."' 4 | 5 | runs: 6 | using: 'what-is-this-runner' 7 | main: 'index.js' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_action_invalid_runners/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./old_node 8 | - uses: ./missing_runs 9 | - uses: ./missing_using 10 | - uses: ./unknown_runner 11 | - uses: ./invalid_node_version 12 | - uses: ./node12 13 | # Check using same invalid action twice does not duplicate the errors 14 | - uses: ./old_node 15 | -------------------------------------------------------------------------------- /testdata/projects/local_composite_action/all_invalid_keys/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Composite action' 2 | author: 'rhysd ' 3 | description: 'Correct composite action' 4 | 5 | runs: 6 | using: 'composite' 7 | steps: 8 | - run: echo hello 9 | main: index.js 10 | pre: pre.js 11 | pre-if: true 12 | post: post.js 13 | post-if: true 14 | image: 'docker://debian:stretch-slim' 15 | pre-entrypoint: pre.sh 16 | entrypoint: main.sh 17 | post-entrypoint: post.sh 18 | args: [foo, bar] 19 | env: 20 | FOO: BAR 21 | -------------------------------------------------------------------------------- /testdata/projects/local_composite_action/missing_steps/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Composite action' 2 | author: 'rhysd ' 3 | description: 'Correct composite action' 4 | 5 | runs: 6 | using: 'composite' 7 | -------------------------------------------------------------------------------- /testdata/projects/local_composite_action/ok/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Composite action' 2 | author: 'rhysd ' 3 | description: 'Correct composite action' 4 | 5 | runs: 6 | using: 'composite' 7 | steps: 8 | - run: echo hello 9 | -------------------------------------------------------------------------------- /testdata/projects/local_composite_action/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./ok 8 | - uses: ./missing_steps 9 | - uses: ./all_invalid_keys 10 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/all_invalid_keys/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Incorrect Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'docker://debian:stretch-slim' 8 | main: index.js 9 | pre: pre.js 10 | pre-if: true 11 | post: post.js 12 | post-if: true 13 | steps: 14 | - run: echo hello 15 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/invalid_dockerfile/Dockerfile2: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/invalid_dockerfile/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Incorrect Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'Dockerfile2' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/missing_files/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Incorrect Docker action' 4 | 5 | runs: 6 | using: docker 7 | image: Dockerfile 8 | pre-entrypoint: pre.sh 9 | entrypoint: main.sh 10 | post-entrypoint: post.sh 11 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/missing_image/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Incorrect Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_all_keys/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_all_keys/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Correct Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'Dockerfile' 8 | pre-entrypoint: pre.sh 9 | entrypoint: main.sh 10 | post-entrypoint: post.sh 11 | args: [foo, bar] 12 | env: 13 | FOO: BAR 14 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_all_keys/main.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_all_keys/post.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_all_keys/pre.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_docker_docker.io/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Correct Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'docker.io/rhysd/actionlint:latest' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_docker_ghcr.io/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Correct Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'ghcr.io/rhysd/actionlint:latest' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_dockerfile/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_dockerfile/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Correct Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'Dockerfile' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_dockerfile_subdir/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Correct Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'subdir/Dockerfile' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_dockerfile_subdir/subdir/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/ok_registry/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Docker action' 2 | author: 'rhysd ' 3 | description: 'Correct Docker action' 4 | 5 | runs: 6 | using: 'docker' 7 | image: 'docker://debian:stretch-slim' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_docker_action/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./ok_dockerfile 8 | - uses: ./ok_registry 9 | - uses: ./ok_all_keys 10 | - uses: ./missing_image 11 | - uses: ./all_invalid_keys 12 | - uses: ./missing_files 13 | - uses: ./ok_dockerfile_subdir 14 | - uses: ./invalid_dockerfile 15 | - uses: ./ok_docker_ghcr.io 16 | - uses: ./ok_docker_docker.io 17 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/all_invalid_keys/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'Composite action' 2 | author: 'rhysd ' 3 | description: 'Correct composite action' 4 | 5 | runs: 6 | using: node20 7 | main: index.js 8 | steps: 9 | - run: echo hello 10 | image: 'docker://debian:stretch-slim' 11 | pre-entrypoint: pre.sh 12 | entrypoint: main.sh 13 | post-entrypoint: post.sh 14 | args: [foo, bar] 15 | env: 16 | FOO: BAR 17 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/all_invalid_keys/index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/invalid_if_sections/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'JavaScript action' 2 | author: 'rhysd ' 3 | description: 'Correct JavaScript action' 4 | 5 | runs: 6 | using: node20 7 | main: index.js 8 | pre-if: true 9 | post-if: true 10 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/invalid_if_sections/index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/missing_files/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'JavaScript action' 2 | author: 'rhysd ' 3 | description: 'Correct JavaScript action' 4 | 5 | runs: 6 | using: node20 7 | main: index.js 8 | pre: pre.js 9 | post: pre.js 10 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/missing_main/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'JavaScript action' 2 | author: 'rhysd ' 3 | description: 'Correct JavaScript action' 4 | 5 | runs: 6 | using: node20 7 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/ok/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'JavaScript action' 2 | author: 'rhysd ' 3 | description: 'Correct JavaScript action' 4 | 5 | runs: 6 | using: 'node20' 7 | main: 'index.js' 8 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/ok/index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/ok_all_optional/action.yaml: -------------------------------------------------------------------------------- 1 | name: 'JavaScript action' 2 | author: 'rhysd ' 3 | description: 'Correct JavaScript action' 4 | 5 | runs: 6 | using: node20 7 | main: index.js 8 | pre: pre.js 9 | pre-if: true 10 | post: pre.js 11 | post-if: true 12 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/ok_all_optional/index.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/ok_all_optional/post.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/ok_all_optional/pre.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testdata/projects/local_javascript_action/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: ./ok 8 | - uses: ./ok_all_optional 9 | - uses: ./missing_main 10 | - uses: ./all_invalid_keys 11 | - uses: ./missing_files 12 | - uses: ./invalid_if_sections 13 | -------------------------------------------------------------------------------- /testdata/projects/paths_config.out: -------------------------------------------------------------------------------- 1 | /workflows/foo\.yaml:11:14: label "unknown" is unknown. available labels are .+ \[runner-label\]/ 2 | /workflows/nested/piyo\.yaml:6:12: context "env" is not allowed here. available contexts are .+ \[expression\]/ 3 | /workflows/nested/piyo\.yaml:11:14: label "unknown" is unknown. available labels are .+ \[runner-label\]/ 4 | -------------------------------------------------------------------------------- /testdata/projects/paths_config/actionlint.yaml: -------------------------------------------------------------------------------- 1 | paths: 2 | workflows/**/*.yaml: 3 | ignore: 4 | - unknown Webhook event ".+" 5 | workflows/*.yaml: 6 | ignore: 7 | - context ".+" is not allowed here 8 | workflows/bar.yaml: 9 | ignore: 10 | - label ".+" is unknown\. available labels are 11 | -------------------------------------------------------------------------------- /testdata/projects/paths_config/workflows/bar.yaml: -------------------------------------------------------------------------------- 1 | # This error will be ignored by workflows/**/*.yaml config 2 | on: unknown 3 | 4 | # This error will be ignored by workflows/*.yaml config 5 | env: 6 | FOO: ${{ env.FOO }} 7 | 8 | jobs: 9 | test: 10 | # This error will be ignored by workflows/bar.yaml config 11 | runs-on: unknown 12 | steps: 13 | - run: echo 14 | -------------------------------------------------------------------------------- /testdata/projects/paths_config/workflows/foo.yaml: -------------------------------------------------------------------------------- 1 | # This error will be ignored by workflows/**/*.yaml config 2 | on: unknown 3 | 4 | # This error will be ignored by workflows/*.yaml config 5 | env: 6 | FOO: ${{ env.FOO }} 7 | 8 | jobs: 9 | test: 10 | # This error will be reported 11 | runs-on: unknown 12 | steps: 13 | - run: echo 14 | -------------------------------------------------------------------------------- /testdata/projects/paths_config/workflows/nested/piyo.yaml: -------------------------------------------------------------------------------- 1 | # This error will be ignored by workflows/**/*.yaml config 2 | on: unknown 3 | 4 | # This error will be reported 5 | env: 6 | FOO: ${{ env.FOO }} 7 | 8 | jobs: 9 | test: 10 | # This error will be reported 11 | runs-on: unknown 12 | steps: 13 | - run: echo 14 | -------------------------------------------------------------------------------- /testdata/projects/recursive_workflow_call.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/recursive_workflow_call.out -------------------------------------------------------------------------------- /testdata/projects/recursive_workflow_call/workflows/recursive.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | input1: 5 | type: string 6 | input2: 7 | type: number 8 | required: true 9 | outputs: 10 | output1: 11 | value: '...' 12 | secrets: 13 | secret1: 14 | secret2: 15 | required: true 16 | 17 | jobs: 18 | caller: 19 | uses: ./workflows/recursive.yaml 20 | with: 21 | input1: hello 22 | input2: 42 23 | secrets: 24 | secret1: aaa 25 | secret2: bbb 26 | other: 27 | needs: [caller] 28 | runs-on: ubuntu-latest 29 | steps: 30 | - run: echo '${{ needs.caller.outputs.output1 }}' 31 | -------------------------------------------------------------------------------- /testdata/projects/user_defined_runner_label.out: -------------------------------------------------------------------------------- 1 | /workflows/test\.yaml:25:14: label "label3" is unknown\. .+\[runner-label\]/ 2 | -------------------------------------------------------------------------------- /testdata/projects/user_defined_runner_label/actionlint.yaml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 2 | labels: ['label1', 'label2', 'glob-*-label'] 3 | config-variables: null 4 | -------------------------------------------------------------------------------- /testdata/projects/user_defined_runner_label/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | ok1: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 8 | ok2: 9 | runs-on: label1 10 | steps: 11 | - run: echo 12 | ok3: 13 | runs-on: label2 14 | steps: 15 | - run: echo 16 | ok4: 17 | runs-on: glob-super-label 18 | steps: 19 | - run: echo 20 | ok5: 21 | runs-on: glob-awesome-great-label 22 | steps: 23 | - run: echo 24 | err: 25 | runs-on: label3 26 | steps: 27 | - run: echo 28 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_inherit_secrets.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/workflow_call_inherit_secrets.out -------------------------------------------------------------------------------- /testdata/projects/workflow_call_inherit_secrets/workflows/reusable.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | secrets: 4 | bar: 5 | required: false 6 | piyo: 7 | required: true 8 | foo: 9 | 10 | jobs: 11 | callee: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - run: hello 15 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_inherit_secrets/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller: 5 | uses: ./workflows/reusable.yaml 6 | secrets: inherit 7 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_input_type_check/workflows/reusable.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | str_input: 5 | type: string 6 | bool_input: 7 | type: boolean 8 | num_input: 9 | type: number 10 | broken_input: 11 | description: type is missing 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - run: 'bye' 18 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_input_type_check/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller1: 5 | uses: ./workflows/reusable.yaml 6 | with: 7 | # Note: any value can be converted into bool 8 | str_input: null 9 | num_input: false 10 | bool_input: 'foo!' 11 | broken_input: null 12 | caller2: 13 | uses: ./workflows/reusable.yaml 14 | with: 15 | str_input: ${{ true }} 16 | num_input: ${{ 'foo' }} 17 | broken_input: 42 18 | caller3: 19 | uses: ./workflows/reusable.yaml 20 | with: 21 | str_input: 22 | num_input: 23 | broken_input: 'hello' 24 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_missing_required.out: -------------------------------------------------------------------------------- 1 | workflows/test.yaml:5:11: input "required1" is required by "./workflows/reusable.yaml" reusable workflow [workflow-call] 2 | workflows/test.yaml:5:11: secret "required1" is required by "./workflows/reusable.yaml" reusable workflow [workflow-call] 3 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_missing_required/workflows/reusable.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | optional1: 5 | type: string 6 | optional2: 7 | type: string 8 | default: foo! 9 | optional3: 10 | type: string 11 | required: false 12 | required1: 13 | type: number 14 | required: true 15 | secrets: 16 | optional1: 17 | optional2: 18 | required: false 19 | required1: 20 | required: true 21 | 22 | jobs: 23 | test: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - run: 'bye' 27 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_missing_required/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller: 5 | uses: ./workflows/reusable.yaml 6 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_not_found.out: -------------------------------------------------------------------------------- 1 | /workflows/test\.yaml:5:11: could not read reusable workflow file for "\./workflows/this-workflow-does-not-exist\.yaml": .+ \[(expression|workflow-call)\]/ 2 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_not_found/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller: 5 | uses: ./workflows/this-workflow-does-not-exist.yaml 6 | other: 7 | needs: [caller] 8 | runs-on: ubuntu-latest 9 | steps: 10 | - run: echo 'Unknown output ${{ needs.caller.outputs.foo }}' 11 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_ok.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/projects/workflow_call_ok.out -------------------------------------------------------------------------------- /testdata/projects/workflow_call_ok/workflows/empty1.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - run: echo hello 9 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_ok/workflows/empty2.yaml: -------------------------------------------------------------------------------- 1 | on: workflow_call 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo hello 8 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_ok/workflows/empty3.yaml: -------------------------------------------------------------------------------- 1 | on: [workflow_call] 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo hello 8 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_ok/workflows/reusable_all_optional.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | str: 5 | type: string 6 | default: hi 7 | num: 8 | type: number 9 | default: 42 10 | bool: 11 | type: boolean 12 | default: true 13 | outputs: 14 | output1: 15 | value: ${{ jobs.run.outputs.message }} 16 | secrets: 17 | foo: 18 | 19 | jobs: 20 | run: 21 | runs-on: ubuntu-latest 22 | outputs: 23 | message: '${{ inputs.str }}, ${{ inputs.num }}, ${{ inputs.bool }}, ${{ secrets.foo }}' 24 | steps: 25 | - run: echo hello 26 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_ok/workflows/reusable_all_required.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | str: 5 | type: string 6 | required: true 7 | num: 8 | type: number 9 | required: true 10 | bool: 11 | type: boolean 12 | required: true 13 | outputs: 14 | output1: 15 | value: ${{ jobs.run.outputs.message }} 16 | secrets: 17 | foo: 18 | required: true 19 | 20 | jobs: 21 | run: 22 | runs-on: ubuntu-latest 23 | outputs: 24 | message: '${{ inputs.str }}, ${{ inputs.num }}, ${{ inputs.bool }}, ${{ secrets.foo }}' 25 | steps: 26 | - run: echo hello 27 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_undefined.out: -------------------------------------------------------------------------------- 1 | workflows/test.yaml:7:7: input "aaa" is not defined in "./workflows/reusable.yaml" reusable workflow. defined input is "foo" [workflow-call] 2 | workflows/test.yaml:10:7: secret "bbb" is not defined in "./workflows/reusable.yaml" reusable workflow. defined secret is "piyo" [workflow-call] 3 | workflows/test.yaml:17:24: property "ccc" is not defined in object type {bar: string} [expression] 4 | workflows/test.yaml:21:7: input "input1" is not defined in "./workflows/empty_reusable.yaml" reusable workflow. no input is defined [workflow-call] 5 | workflows/test.yaml:23:7: secret "secret1" is not defined in "./workflows/empty_reusable.yaml" reusable workflow. no secret is defined [workflow-call] 6 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_undefined/workflows/empty_reusable.yaml: -------------------------------------------------------------------------------- 1 | on: workflow_call 2 | 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo 'hi' 8 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_undefined/workflows/reusable.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | foo: 5 | type: string 6 | outputs: 7 | bar: 8 | value: 'hello' 9 | secrets: 10 | piyo: 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - run: echo 'bye' 17 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_undefined/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | caller: 5 | uses: ./workflows/reusable.yaml 6 | with: 7 | aaa: this is not existing 8 | foo: this is existing 9 | secrets: 10 | bbb: this is not existing 11 | piyo: this is existing 12 | other: 13 | needs: [caller] 14 | runs-on: ubuntu-latest 15 | steps: 16 | - run: echo '${{ needs.caller.outputs.bar }} is existing' 17 | - run: echo '${{ needs.caller.outputs.ccc }} is not existing' 18 | empty: 19 | uses: ./workflows/empty_reusable.yaml 20 | with: 21 | input1: this is not existing 22 | secrets: 23 | secret1: this is not existing 24 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_upper_case/reusable/lower.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | my_input_1: 5 | required: true 6 | my_input_2: 7 | required: true 8 | outputs: 9 | my_output_1: 10 | value: ... 11 | my_output_2: 12 | value: ... 13 | secrets: 14 | my_secret_1: 15 | required: true 16 | my_secret_2: 17 | required: true 18 | 19 | jobs: 20 | test: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - run: echo '${{ inputs.my_input_1 }}' 24 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_upper_case/reusable/upper.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | MY_INPUT_1: 5 | required: true 6 | MY_INPUT_2: 7 | required: true 8 | outputs: 9 | MY_OUTPUT_1: 10 | value: ... 11 | MY_OUTPUT_2: 12 | value: ... 13 | secrets: 14 | MY_SECRET_1: 15 | required: true 16 | MY_SECRET_2: 17 | required: true 18 | 19 | jobs: 20 | test: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - run: echo '${{ inputs.my_input_1 }}' 24 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_upper_case/workflows/missing.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | upper: 5 | uses: ./reusable/upper.yaml 6 | with: 7 | my_input_1: hello 8 | secrets: 9 | my_secret_1: hello 10 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_upper_case/workflows/ok_lower.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | upper: 5 | uses: ./reusable/upper.yaml 6 | with: 7 | my_input_1: hello 8 | my_input_2: world 9 | secrets: 10 | my_secret_1: hello 11 | my_secret_2: world 12 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_upper_case/workflows/ok_upper.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | upper: 5 | uses: ./reusable/upper.yaml 6 | with: 7 | MY_INPUT_1: hello 8 | MY_INPUT_2: world 9 | secrets: 10 | MY_SECRET_1: hello 11 | MY_SECRET_2: world 12 | lower: 13 | uses: ./reusable/lower.yaml 14 | with: 15 | MY_INPUT_1: hello 16 | MY_INPUT_2: world 17 | secrets: 18 | MY_SECRET_1: hello 19 | MY_SECRET_2: world 20 | -------------------------------------------------------------------------------- /testdata/projects/workflow_call_upper_case/workflows/undefined.yaml: -------------------------------------------------------------------------------- 1 | on: push 2 | 3 | jobs: 4 | upper: 5 | uses: ./reusable/upper.yaml 6 | with: 7 | MY_INPUT_1: hello 8 | MY_INPUT_2: world 9 | MY_INPUT_3: undefined 10 | secrets: 11 | MY_SECRET_1: hello 12 | MY_SECRET_2: world 13 | MY_SECRET_3: undefined 14 | lower: 15 | uses: ./reusable/lower.yaml 16 | with: 17 | MY_INPUT_1: hello 18 | MY_INPUT_2: world 19 | MY_INPUT_3: undefined 20 | secrets: 21 | MY_SECRET_1: hello 22 | MY_SECRET_2: world 23 | MY_SECRET_3: undefined 24 | -------------------------------------------------------------------------------- /testdata/realworld/.gitignore: -------------------------------------------------------------------------------- 1 | /*.yaml 2 | /*.yml 3 | -------------------------------------------------------------------------------- /testdata/realworld/dataset.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhysd/actionlint/2ab3a12c7848f6c15faca9a92612ef4261d0e370/testdata/realworld/dataset.zip -------------------------------------------------------------------------------- /testdata/reusable_workflow_metadata/broken.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | outputs: my cool outputs 4 | -------------------------------------------------------------------------------- /testdata/reusable_workflow_metadata/broken_inputs.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | inputs: 4 | validate: validate something when true 5 | -------------------------------------------------------------------------------- /testdata/reusable_workflow_metadata/broken_secrets.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_call: 3 | secrets: 4 | foo: my secret 5 | -------------------------------------------------------------------------------- /testdata/reusable_workflow_metadata/no_hook.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | -------------------------------------------------------------------------------- /testdata/reusable_workflow_metadata/no_on.yaml: -------------------------------------------------------------------------------- 1 | jobs: 2 | test: 3 | foo: 4 | runs-on: ubuntu-latest 5 | steps: 6 | - run: echo ... 7 | -------------------------------------------------------------------------------- /testdata/reusable_workflow_metadata/ok.yaml: -------------------------------------------------------------------------------- 1 | name: minimal 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | input1: 7 | description: input 8 | type: string 9 | input2: 10 | type: boolean 11 | required: true 12 | outputs: 13 | output1: 14 | description: output 15 | value: foo 16 | secrets: 17 | secret1: 18 | description: secret 19 | secret2: 20 | required: true 21 | 22 | jobs: 23 | test: 24 | runs-on: ubuntu-18.04 25 | steps: 26 | - run: echo ... 27 | --------------------------------------------------------------------------------