Pipelines: pass relative artifact paths to child jobs (#24085)
Passing absolute paths from pipeline generate job to downstream rebuild jobs causes problems when the CI_PROJECT_DIR is not the same for the generate and rebuild jobs. This has happened, for example, when gitlab checks out the project into a runner-specific directory and different runners are chosen for the generate and rebuild jobs.
This commit is contained in:
		| @@ -642,6 +642,25 @@ def generate_gitlab_ci_yaml(env, print_summary, output_file, | ||||
|     local_mirror_dir = os.path.join(pipeline_artifacts_dir, 'mirror') | ||||
|     user_artifacts_dir = os.path.join(pipeline_artifacts_dir, 'user_data') | ||||
| 
 | ||||
|     # We communicate relative paths to the downstream jobs to avoid issues in | ||||
|     # situations where the CI_PROJECT_DIR varies between the pipeline | ||||
|     # generation job and the rebuild jobs.  This can happen when gitlab | ||||
|     # checks out the project into a runner-specific directory, for example, | ||||
|     # and different runners are picked for generate and rebuild jobs. | ||||
|     ci_project_dir = os.environ.get('CI_PROJECT_DIR') | ||||
|     rel_artifacts_root = os.path.relpath( | ||||
|         pipeline_artifacts_dir, ci_project_dir) | ||||
|     rel_concrete_env_dir = os.path.relpath( | ||||
|         concrete_env_dir, ci_project_dir) | ||||
|     rel_job_log_dir = os.path.relpath( | ||||
|         job_log_dir, ci_project_dir) | ||||
|     rel_job_repro_dir = os.path.relpath( | ||||
|         job_repro_dir, ci_project_dir) | ||||
|     rel_local_mirror_dir = os.path.relpath( | ||||
|         local_mirror_dir, ci_project_dir) | ||||
|     rel_user_artifacts_dir = os.path.relpath( | ||||
|         user_artifacts_dir, ci_project_dir) | ||||
| 
 | ||||
|     # Speed up staging by first fetching binary indices from all mirrors | ||||
|     # (including the per-PR mirror we may have just added above). | ||||
|     bindist.binary_index.update() | ||||
| @@ -884,9 +903,9 @@ def generate_gitlab_ci_yaml(env, print_summary, output_file, | ||||
|                 variables.update(job_vars) | ||||
| 
 | ||||
|                 artifact_paths = [ | ||||
|                     job_log_dir, | ||||
|                     job_repro_dir, | ||||
|                     user_artifacts_dir | ||||
|                     rel_job_log_dir, | ||||
|                     rel_job_repro_dir, | ||||
|                     rel_user_artifacts_dir | ||||
|                 ] | ||||
| 
 | ||||
|                 if enable_artifacts_buildcache: | ||||
| @@ -1042,14 +1061,14 @@ def generate_gitlab_ci_yaml(env, print_summary, output_file, | ||||
|                 version_to_clone = spack_version | ||||
| 
 | ||||
|         output_object['variables'] = { | ||||
|             'SPACK_ARTIFACTS_ROOT': pipeline_artifacts_dir, | ||||
|             'SPACK_CONCRETE_ENV_DIR': concrete_env_dir, | ||||
|             'SPACK_ARTIFACTS_ROOT': rel_artifacts_root, | ||||
|             'SPACK_CONCRETE_ENV_DIR': rel_concrete_env_dir, | ||||
|             'SPACK_VERSION': spack_version, | ||||
|             'SPACK_CHECKOUT_VERSION': version_to_clone, | ||||
|             'SPACK_REMOTE_MIRROR_URL': remote_mirror_url, | ||||
|             'SPACK_JOB_LOG_DIR': job_log_dir, | ||||
|             'SPACK_JOB_REPRO_DIR': job_repro_dir, | ||||
|             'SPACK_LOCAL_MIRROR_DIR': local_mirror_dir, | ||||
|             'SPACK_JOB_LOG_DIR': rel_job_log_dir, | ||||
|             'SPACK_JOB_REPRO_DIR': rel_job_repro_dir, | ||||
|             'SPACK_LOCAL_MIRROR_DIR': rel_local_mirror_dir, | ||||
|             'SPACK_IS_PR_PIPELINE': str(is_pr_pipeline) | ||||
|         } | ||||
| 
 | ||||
| @@ -1535,7 +1554,6 @@ def reproduce_ci_job(url, work_dir): | ||||
|         tty.debug('  {0}'.format(yaml_file)) | ||||
| 
 | ||||
|     pipeline_yaml = None | ||||
|     pipeline_variables = None | ||||
| 
 | ||||
|     # Try to find the dynamically generated pipeline yaml file in the | ||||
|     # reproducer.  If the user did not put it in the artifacts root, | ||||
| @@ -1546,7 +1564,6 @@ def reproduce_ci_job(url, work_dir): | ||||
|             yaml_obj = syaml.load(y_fd) | ||||
|             if 'variables' in yaml_obj and 'stages' in yaml_obj: | ||||
|                 pipeline_yaml = yaml_obj | ||||
|                 pipeline_variables = pipeline_yaml['variables'] | ||||
| 
 | ||||
|     if pipeline_yaml: | ||||
|         tty.debug('\n{0} is likely your pipeline file'.format(yf)) | ||||
| @@ -1603,9 +1620,8 @@ def reproduce_ci_job(url, work_dir): | ||||
|         # more faithful reproducer if everything appears to run in the same | ||||
|         # absolute path used during the CI build. | ||||
|         mount_as_dir = '/work' | ||||
|         if pipeline_variables: | ||||
|             artifacts_root = pipeline_variables['SPACK_ARTIFACTS_ROOT'] | ||||
|             mount_as_dir = os.path.dirname(artifacts_root) | ||||
|         if repro_details: | ||||
|             mount_as_dir = repro_details['ci_project_dir'] | ||||
|             mounted_repro_dir = os.path.join(mount_as_dir, rel_repro_dir) | ||||
| 
 | ||||
|         # We will also try to clone spack from your local checkout and | ||||
|   | ||||
| @@ -201,6 +201,15 @@ def ci_rebuild(args): | ||||
|     pr_mirror_url = get_env_var('SPACK_PR_MIRROR_URL') | ||||
|     remote_mirror_url = get_env_var('SPACK_REMOTE_MIRROR_URL') | ||||
| 
 | ||||
|     # Construct absolute paths relative to current $CI_PROJECT_DIR | ||||
|     ci_project_dir = get_env_var('CI_PROJECT_DIR') | ||||
|     pipeline_artifacts_dir = os.path.join( | ||||
|         ci_project_dir, pipeline_artifacts_dir) | ||||
|     job_log_dir = os.path.join(ci_project_dir, job_log_dir) | ||||
|     repro_dir = os.path.join(ci_project_dir, repro_dir) | ||||
|     local_mirror_dir = os.path.join(ci_project_dir, local_mirror_dir) | ||||
|     concrete_env_dir = os.path.join(ci_project_dir, concrete_env_dir) | ||||
| 
 | ||||
|     # Debug print some of the key environment variables we should have received | ||||
|     tty.debug('pipeline_artifacts_dir = {0}'.format(pipeline_artifacts_dir)) | ||||
|     tty.debug('root_spec = {0}'.format(root_spec)) | ||||
| @@ -342,7 +351,8 @@ def ci_rebuild(args): | ||||
|     repro_details = { | ||||
|         'job_name': ci_job_name, | ||||
|         'job_spec_yaml': job_spec_yaml_file, | ||||
|         'root_spec_yaml': 'root.yaml' | ||||
|         'root_spec_yaml': 'root.yaml', | ||||
|         'ci_project_dir': ci_project_dir | ||||
|     } | ||||
|     with open(repro_file, 'w') as fd: | ||||
|         fd.write(json.dumps(repro_details)) | ||||
|   | ||||
| @@ -41,6 +41,17 @@ | ||||
| pytestmark = pytest.mark.maybeslow | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def project_dir_env(): | ||||
|     def _set_project_dir(path): | ||||
|         os.environ['CI_PROJECT_DIR'] = path | ||||
| 
 | ||||
|     yield _set_project_dir | ||||
| 
 | ||||
|     if 'CI_PROJECT_DIR' in os.environ: | ||||
|         os.environ.pop('CI_PROJECT_DIR') | ||||
| 
 | ||||
| 
 | ||||
| @pytest.fixture() | ||||
| def env_deactivate(): | ||||
|     yield | ||||
| @@ -112,9 +123,10 @@ def test_specs_staging(config): | ||||
| 
 | ||||
| 
 | ||||
| def test_ci_generate_with_env(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|                               install_mockery, mock_packages): | ||||
|                               install_mockery, mock_packages, project_dir_env): | ||||
|     """Make sure we can get a .gitlab-ci.yml from an environment file | ||||
|        which has the gitlab-ci, cdash, and mirrors sections.""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     mirror_url = 'https://my.fake.mirror' | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
| @@ -183,6 +195,11 @@ def test_ci_generate_with_env(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|                 mirror_url) | ||||
|             assert(rebuild_job['script'][0] == expected) | ||||
| 
 | ||||
|             assert('variables' in yaml_contents) | ||||
|             assert('SPACK_ARTIFACTS_ROOT' in yaml_contents['variables']) | ||||
|             artifacts_root = yaml_contents['variables']['SPACK_ARTIFACTS_ROOT'] | ||||
|             assert(artifacts_root == 'jobs_scratch_dir') | ||||
| 
 | ||||
| 
 | ||||
| def _validate_needs_graph(yaml_contents, needs_graph, artifacts): | ||||
|     for job_name, job_def in yaml_contents.items(): | ||||
| @@ -202,9 +219,10 @@ def _validate_needs_graph(yaml_contents, needs_graph, artifacts): | ||||
| 
 | ||||
| def test_ci_generate_bootstrap_gcc(tmpdir, mutable_mock_env_path, | ||||
|                                    env_deactivate, install_mockery, | ||||
|                                    mock_packages): | ||||
|                                    mock_packages, project_dir_env): | ||||
|     """Test that we can bootstrap a compiler and use it as the | ||||
|     compiler for a spec in the environment""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -265,9 +283,11 @@ def test_ci_generate_bootstrap_artifacts_buildcache(tmpdir, | ||||
|                                                     mutable_mock_env_path, | ||||
|                                                     env_deactivate, | ||||
|                                                     install_mockery, | ||||
|                                                     mock_packages): | ||||
|                                                     mock_packages, | ||||
|                                                     project_dir_env): | ||||
|     """Test that we can bootstrap a compiler when artifacts buildcache | ||||
|     is turned on""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -329,8 +349,9 @@ def test_ci_generate_bootstrap_artifacts_buildcache(tmpdir, | ||||
| 
 | ||||
| def test_ci_generate_with_env_missing_section(tmpdir, mutable_mock_env_path, | ||||
|                                               env_deactivate, install_mockery, | ||||
|                                               mock_packages): | ||||
|                                               mock_packages, project_dir_env): | ||||
|     """Make sure we get a reasonable message if we omit gitlab-ci section""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -353,8 +374,9 @@ def test_ci_generate_with_env_missing_section(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_with_cdash_token(tmpdir, mutable_mock_env_path, | ||||
|                                       env_deactivate, install_mockery, | ||||
|                                       mock_packages): | ||||
|                                       mock_packages, project_dir_env): | ||||
|     """Make sure we it doesn't break if we configure cdash""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -407,8 +429,10 @@ def test_ci_generate_with_cdash_token(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_with_custom_scripts(tmpdir, mutable_mock_env_path, | ||||
|                                          env_deactivate, install_mockery, | ||||
|                                          mock_packages, monkeypatch): | ||||
|                                          mock_packages, monkeypatch, | ||||
|                                          project_dir_env): | ||||
|     """Test use of user-provided scripts""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -496,8 +520,9 @@ def test_ci_generate_with_custom_scripts(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_pkg_with_deps(tmpdir, mutable_mock_env_path, | ||||
|                                    env_deactivate, install_mockery, | ||||
|                                    mock_packages): | ||||
|                                    mock_packages, project_dir_env): | ||||
|     """Test pipeline generation for a package w/ dependencies""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -549,10 +574,12 @@ def test_ci_generate_pkg_with_deps(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_for_pr_pipeline(tmpdir, mutable_mock_env_path, | ||||
|                                      env_deactivate, install_mockery, | ||||
|                                      mock_packages, monkeypatch): | ||||
|                                      mock_packages, monkeypatch, | ||||
|                                      project_dir_env): | ||||
|     """Test that PR pipelines do not include a final stage job for | ||||
|     rebuilding the mirror index, even if that job is specifically | ||||
|     configured""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -609,8 +636,10 @@ def test_ci_generate_for_pr_pipeline(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_with_external_pkg(tmpdir, mutable_mock_env_path, | ||||
|                                        env_deactivate, install_mockery, | ||||
|                                        mock_packages, monkeypatch): | ||||
|                                        mock_packages, monkeypatch, | ||||
|                                        project_dir_env): | ||||
|     """Make sure we do not generate jobs for external pkgs""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -649,7 +678,8 @@ def test_ci_generate_with_external_pkg(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_rebuild(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|                     install_mockery, mock_packages, monkeypatch, | ||||
|                     mock_gnupghome, mock_fetch): | ||||
|                     mock_gnupghome, mock_fetch, project_dir_env): | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     working_dir = tmpdir.join('working_dir') | ||||
| 
 | ||||
|     log_dir = os.path.join(working_dir.strpath, 'logs') | ||||
| @@ -790,7 +820,8 @@ def mystrip(s): | ||||
| 
 | ||||
| def test_ci_nothing_to_rebuild(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|                                install_mockery, mock_packages, monkeypatch, | ||||
|                                mock_fetch): | ||||
|                                mock_fetch, project_dir_env): | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     working_dir = tmpdir.join('working_dir') | ||||
| 
 | ||||
|     mirror_dir = working_dir.join('mirror') | ||||
| @@ -864,7 +895,8 @@ def fake_dl_method(spec, dest, require_cdashid, m_url=None): | ||||
| @pytest.mark.disable_clean_stage_check | ||||
| def test_push_mirror_contents(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|                               install_mockery, mock_packages, mock_fetch, | ||||
|                               mock_stage, mock_gnupghome): | ||||
|                               mock_stage, mock_gnupghome, project_dir_env): | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     working_dir = tmpdir.join('working_dir') | ||||
| 
 | ||||
|     mirror_dir = working_dir.join('mirror') | ||||
| @@ -1033,11 +1065,13 @@ def faked(env, spec_yaml=None, packages=None, add_spec=True, | ||||
| 
 | ||||
| def test_ci_generate_override_runner_attrs(tmpdir, mutable_mock_env_path, | ||||
|                                            env_deactivate, install_mockery, | ||||
|                                            mock_packages, monkeypatch): | ||||
|                                            mock_packages, monkeypatch, | ||||
|                                            project_dir_env): | ||||
|     """Test that we get the behavior we want with respect to the provision | ||||
|        of runner attributes like tags, variables, and scripts, both when we | ||||
|        inherit them from the top level, as well as when we override one or | ||||
|        more at the runner level""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -1174,8 +1208,10 @@ def test_ci_generate_override_runner_attrs(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_with_workarounds(tmpdir, mutable_mock_env_path, | ||||
|                                       env_deactivate, install_mockery, | ||||
|                                       mock_packages, monkeypatch): | ||||
|                                       mock_packages, monkeypatch, | ||||
|                                       project_dir_env): | ||||
|     """Make sure the post-processing cli workarounds do what they should""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -1274,13 +1310,14 @@ def test_ci_rebuild_index(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
| def test_ci_generate_bootstrap_prune_dag( | ||||
|         install_mockery_mutable_config, mock_packages, mock_fetch, | ||||
|         mock_archive, mutable_config, monkeypatch, tmpdir, | ||||
|         mutable_mock_env_path, env_deactivate): | ||||
|         mutable_mock_env_path, env_deactivate, project_dir_env): | ||||
|     """Test compiler bootstrapping with DAG pruning.  Specifically, make | ||||
|        sure that if we detect the bootstrapped compiler needs to be rebuilt, | ||||
|        we ensure the spec we want to build with that compiler is scheduled | ||||
|        for rebuild as well.""" | ||||
| 
 | ||||
|     # Create a temp mirror directory for buildcache usage | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     mirror_dir = tmpdir.join('mirror_dir') | ||||
|     mirror_url = 'file://{0}'.format(mirror_dir.strpath) | ||||
| 
 | ||||
| @@ -1404,8 +1441,9 @@ def fake_get_mirrors_for_spec(spec=None, full_hash_match=False, | ||||
| 
 | ||||
| def test_ci_subcommands_without_mirror(tmpdir, mutable_mock_env_path, | ||||
|                                        env_deactivate, mock_packages, | ||||
|                                        install_mockery): | ||||
|                                        install_mockery, project_dir_env): | ||||
|     """Make sure we catch if there is not a mirror and report an error""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -1481,8 +1519,10 @@ def test_ensure_only_one_temporary_storage(): | ||||
| 
 | ||||
| def test_ci_generate_temp_storage_url(tmpdir, mutable_mock_env_path, | ||||
|                                       env_deactivate, install_mockery, | ||||
|                                       mock_packages, monkeypatch): | ||||
|                                       mock_packages, monkeypatch, | ||||
|                                       project_dir_env): | ||||
|     """Verify correct behavior when using temporary-storage-url-prefix""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     filename = str(tmpdir.join('spack.yaml')) | ||||
|     with open(filename, 'w') as f: | ||||
|         f.write("""\ | ||||
| @@ -1533,8 +1573,10 @@ def test_ci_generate_temp_storage_url(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_generate_read_broken_specs_url(tmpdir, mutable_mock_env_path, | ||||
|                                            env_deactivate, install_mockery, | ||||
|                                            mock_packages, monkeypatch): | ||||
|                                            mock_packages, monkeypatch, | ||||
|                                            project_dir_env): | ||||
|     """Verify that `broken-specs-url` works as intended""" | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     spec_a = Spec('a') | ||||
|     spec_a.concretize() | ||||
|     a_full_hash = spec_a.full_hash() | ||||
| @@ -1585,7 +1627,8 @@ def test_ci_generate_read_broken_specs_url(tmpdir, mutable_mock_env_path, | ||||
| 
 | ||||
| def test_ci_reproduce(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|                       install_mockery, mock_packages, monkeypatch, | ||||
|                       last_two_git_commits): | ||||
|                       last_two_git_commits, project_dir_env): | ||||
|     project_dir_env(tmpdir.strpath) | ||||
|     working_dir = tmpdir.join('repro_dir') | ||||
|     image_name = 'org/image:tag' | ||||
| 
 | ||||
| @@ -1657,7 +1700,8 @@ def test_ci_reproduce(tmpdir, mutable_mock_env_path, env_deactivate, | ||||
|             repro_details = { | ||||
|                 'job_name': job_name, | ||||
|                 'job_spec_yaml': 'archivefiles.yaml', | ||||
|                 'root_spec_yaml': 'root.yaml' | ||||
|                 'root_spec_yaml': 'root.yaml', | ||||
|                 'ci_project_dir': working_dir.strpath | ||||
|             } | ||||
|             with open(repro_file, 'w') as fd: | ||||
|                 fd.write(json.dumps(repro_details)) | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user
	 Scott Wittenburg
					Scott Wittenburg