Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build tool update #79

Merged
merged 31 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7d8db1c
Sets a default status
jasonb5 Jun 2, 2020
2563202
Adds additional cli args to modify behavior.
jasonb5 Jun 2, 2020
2832c95
Fixes issue where extra_channels is None rather than an empty list
jasonb5 Jun 2, 2020
e86008c
Fixes issue trying to create base env. Will create if not base otherw…
jasonb5 Jun 2, 2020
3c5ed9f
Fixes passing env to build command
jasonb5 Jun 2, 2020
ca3b558
Fixes passing extra argument to format
jasonb5 Jun 3, 2020
68585c1
Adds argument to pass path to conda activate incase it cannot be found
jasonb5 Jun 3, 2020
8ac49fb
Adds argument to copy conda output package
jasonb5 Jun 3, 2020
b3766cb
Executes conda-info in correct environment and checks to see if conda…
jasonb5 Jun 4, 2020
39ea8d5
Fixes creating a fake feedstock in workdir if the package does not ha…
jasonb5 Jun 4, 2020
5da413c
Adds option to use a local repo to build package
jasonb5 Jun 5, 2020
d246c8f
Removes check for recipe in local repo
jasonb5 Jun 5, 2020
821c350
Fixes feedstock name
jasonb5 Jun 5, 2020
40b4769
Fixes missing format
jasonb5 Jun 5, 2020
c28ce3b
use **kwargs
LinaMuryanto Jun 6, 2020
16b545c
added missing closing paren
LinaMuryanto Jun 6, 2020
b4ba8af
added **kwargs to clone_feedstock()
LinaMuryanto Jun 6, 2020
1a27857
Merge pull request #80 from CDAT/build_tool_update.2
muryanto1 Jun 15, 2020
a9d4095
adding debug
LinaMuryanto Jun 15, 2020
2dce340
added mkdir -p of conda_copy_package directory
LinaMuryanto Jun 15, 2020
e204a50
added debug
LinaMuryanto Jun 15, 2020
3f82bda
added debug
LinaMuryanto Jun 15, 2020
7718db7
fixed DEBUG message
LinaMuryanto Jun 16, 2020
104bf19
when adding channels listed in extra_channels, need to go in reverse …
LinaMuryanto Jun 26, 2020
63decef
restore the code for adding channels
LinaMuryanto Jun 26, 2020
b4d1a89
reverse extra_channels before running conda config --add channels
LinaMuryanto Jun 26, 2020
3a9e8b3
adding README.md
LinaMuryanto Jun 26, 2020
dee6286
update README.md
LinaMuryanto Jun 26, 2020
6114d85
update README.md
LinaMuryanto Jun 26, 2020
2a97a69
removed debug messages
LinaMuryanto Jun 26, 2020
a9410f8
add copy_files_from_repo()
LinaMuryanto Jun 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 78 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,82 @@
# conda-recipes

# Building metapackages
This repository contains tools for:
- CDAT developers to build their conda packages, create a test environment, and upload the built package to conda channel. The tools are under *build_tools*, the main script is *build_tools/conda_build.py*. *conda_build.py* can be called from project's *Makefile* which can then be called from project's *.circleci/config.yml*.

# Notes for CDAT developers

In order to use *build_tools/conda_build.py*, create a conda environment and activate the *base* environment.

## Clone conda-recipes repository.

Clone conda-recipes repository to a work directory (referred as $WORKDIR in this documentation).

```
export WORKDIR=<some_work_directory>
git clone https://github.com/CDAT/conda-recipes $WORKDIR/conda-recipes
export BUILD_SCRIPT=$WORKDIR/conda-recipes/build_tools/conda_build.py
```
Run *build_tools/conda_build.py --help* to get information about this script.

Clone the CDAT project you want to build. For example, to clone *cdms* project:

```
git clone https://github.com/CDAT/cdms
cd cdms
```

Set the following environment variables.
```bash
export PKG_NAME=<package_name>
export REPO_NAME=<repo_name>
export LAST_STABLE=<last_stable_version>
export BRANCH=<project_branch>
export CONDA_ACTIVATE=<conda_path>/bin/activate
export CONDA_ENV=<test_environment_name>
```

For example:
```
export PKG_NAME=cdms2
export REPO_NAME=cdms
export LAST_STABLE=3.1.4
export BRANCH=fix_flake8
export CONDA_ACTIVATE=/home/username/miniconda3/bin/activate
export PYTHON_VERSION=3.7
export CONDA_ENV=test_cdms
```

## Rerender


First step in building a conda package is to do a rerendering which will pick up latest conda-forge update so that we get latest pinned dependencies. You will need *recipe/meta.yaml.in* in the project repo

```bash
$ python $BUILD_SCRIPT --workdir $WORKDIR --last_stable $LAST_STABLE \
--build 0 --package_name $PKG_NAME --repo_name $REPO_NAME \
--branch $BRANCH --do_rerender
```

## Build

```bash
$ export EXTRA_CHANNELS=cdat/label/nightly
$ python $BUILD_SCRIPT --workdir $WORKDIR --package_name $PKG_NAME \
--repo_name $REPO_NAME --build_version $PYTHON_VERSION \
--extra_channels $EXTRA_CHANNELS \
--conda_activate $CONDA_ACTIVATE --do_build
```

## Setup an environment with the built package
```bash
conda create -y -n $CONDA_ENV --use-local -c $EXTRA_CHANNEL $PKG_NAME
```

For example, to create a test environment for running cdms test cases:
```bash
conda create -y -n $CONDA_ENV --use-local -c $EXTRA_CHANNELS $PKG_NAME testsrunner pytest
```



Make sure you have a version of `conda-build` that is at least `3.0.28`. Please run `conda update conda-build` before.

24 changes: 15 additions & 9 deletions build_tools/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
SUCCESS = 0
FAILURE = 1

def run_command(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None):
def run_command(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None, env=None):
print("CMD: {c}".format(c=cmd))
if isinstance(cmd, str):
cmd = shlex.split(cmd)
Expand All @@ -22,8 +22,14 @@ def run_command(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None):
else:
current_wd = cwd

new_env = os.environ.copy()

if env is not None:
new_env.update(env)

P = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr_setting,
bufsize=0, cwd=current_wd, shell=shell_cmd)
bufsize=0, cwd=current_wd, shell=shell_cmd, env=new_env)

out = []
while P.poll() is None:
read = P.stdout.readline().rstrip()
Expand All @@ -34,21 +40,21 @@ def run_command(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None):

ret_code = P.returncode
return(ret_code, out)

def run_cmd(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None):

ret_code, output = run_command(cmd, join_stderr, shell_cmd, verbose, cwd)
def run_cmd(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None, env=None):

ret_code, output = run_command(cmd, join_stderr, shell_cmd, verbose, cwd, env)
return(ret_code)

def run_cmds(cmds, join_stderr=True, shell_cmd=False, verbose=True, cwd=None):
def run_cmds(cmds, join_stderr=True, shell_cmd=False, verbose=True, cwd=None, env=None):
for cmd in cmds:
ret_code, output = run_command(cmd, join_stderr, shell_cmd, verbose, cwd)
ret_code, output = run_command(cmd, join_stderr, shell_cmd, verbose, cwd, env)
if ret_code != SUCCESS:
return ret_code
return ret_code

def run_cmd_capture_output(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None):
def run_cmd_capture_output(cmd, join_stderr=True, shell_cmd=False, verbose=True, cwd=None, env=None):

ret_code, output = run_command(cmd, join_stderr, shell_cmd, verbose, cwd)
ret_code, output = run_command(cmd, join_stderr, shell_cmd, verbose, cwd, env)
return(ret_code, output)

96 changes: 60 additions & 36 deletions build_tools/conda_build.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import glob
import argparse
import os
import sys
import subprocess
import shlex
import shutil
import requests
import time
import re

from Utils import run_cmd, run_cmds, run_cmd_capture_output
from Utils import SUCCESS, FAILURE
from release_tools import find_conda_activate, create_fake_feedstock
from release_tools import prep_conda_env, check_if_conda_forge_pkg, clone_feedstock
from release_tools import clone_repo, prepare_recipe_in_local_feedstock_repo
from release_tools import copy_file_from_repo_recipe
Expand Down Expand Up @@ -48,13 +43,15 @@
# this script in CircleCI.
#

conda_rc = os.path.join(os.getcwd(), "condarc")

parser = argparse.ArgumentParser(
description='conda build upload',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument("-p", "--package_name",
help="Package name to build")
parser.add_argument("-o", "--github_organization_name",
parser.add_argument("-o", "--organization",
help="github organization name", default="CDAT")
parser.add_argument("-r", "--repo_name",
help="repo name to build")
Expand All @@ -69,22 +66,30 @@
parser.add_argument("--do_rerender", action='store_true', help="do 'conda smithy rerender'")
parser.add_argument("--do_build", action='store_true', help="do 'conda build -m <variant file> ...'")
parser.add_argument("--build_version", default="3.7", help="specify python version to build 2.7, 3.7, 3.8")
parser.add_argument("--conda_env", default="base", help="Conda environment to use, will be created if it doesn't exist")
parser.add_argument("--extra_channels", nargs="+", type=str, default=[])
parser.add_argument("--ignore_conda_missmatch", action="store_true", help="Will skip checking if packages are uptodate when rerendering recipe.")
parser.add_argument("--conda_rc", default=conda_rc, help="File to use for condarc")
parser.add_argument("--conda_activate", help="Path to conda activate script.")
parser.add_argument("--copy_conda_package", help="Copies output conda package to directory")
parser.add_argument("--local_repo", help="Path to local project repository")

args = parser.parse_args(sys.argv[1:])

print(args)

pkg_name = args.package_name
branch = args.branch
workdir = args.workdir
build = args.build
do_conda_clean = args.conda_clean
local_repo = args.local_repo

if args.repo_name:
repo_name = args.repo_name
else:
repo_name = pkg_name
if local_repo is not None and not os.path.exists(local_repo):
print("Local repository {} does not exist".format(local_repo))
sys.exit(FAILURE)

# github organization of projects
organization = args.github_organization_name
status = FAILURE

# for calling run_cmds
join_stderr = True
Expand All @@ -108,63 +113,82 @@ def construct_pkg_ver(repo_dir, arg_version, arg_last_stable):
# main
#

kwargs = vars(args)
kwargs["conda_activate"] = args.conda_activate or find_conda_activate()
if kwargs["repo_name"] is None:
kwargs["repo_name"] = pkg_name

repo_name = kwargs["repo_name"]

if kwargs["conda_activate"] is None or not os.path.exists(kwargs["conda_activate"]):
print("Could not find conda activate script, try passing with --conda_activate argument and check file exists")
sys.exit(FAILURE)

is_conda_forge_pkg = check_if_conda_forge_pkg(pkg_name)

status = prep_conda_env(**kwargs)
if status != SUCCESS:
sys.exit(status)

if args.do_rerender:
status = prep_conda_env()
if status != SUCCESS:
sys.exit(status)

ret, repo_dir = clone_repo(organization, repo_name, branch, workdir)
if ret != SUCCESS:
sys.exit(ret)
version = construct_pkg_ver(repo_dir, args.version, args.last_stable)
else:
repo_dir = "{w}/{p}".format(w=workdir, p=repo_name)
if local_repo is None:
ret, repo_dir = clone_repo(**kwargs)
if ret != SUCCESS:
sys.exit(ret)
else:
repo_dir = local_repo

kwargs["version"] = version = construct_pkg_ver(repo_dir, args.version, args.last_stable)
else:
if local_repo is None:
repo_dir = os.path.join(workdir, repo_name)
else:
repo_dir = local_repo

if is_conda_forge_pkg:
if args.do_rerender:
status = clone_feedstock(pkg_name, workdir)
status = clone_feedstock(**kwargs)
if status != SUCCESS:
sys.exit(status)

status = prepare_recipe_in_local_feedstock_repo(pkg_name, organization, repo_name, branch, version, build, repo_dir, workdir)
status = prepare_recipe_in_local_feedstock_repo(pkg_version=version, repo_dir=repo_dir, **kwargs)
if status != SUCCESS:
sys.exit(status)

status = copy_file_from_repo_recipe(pkg_name, repo_dir, workdir,
"conda_build_config.yaml")
status = copy_file_from_repo_recipe(repo_dir=repo_dir, filename="conda_build_config.yaml", **kwargs)
if status != SUCCESS:
sys.exit(status)

status = copy_file_from_repo_recipe(pkg_name, repo_dir, workdir,
"build.sh")
status = copy_file_from_repo_recipe(repo_dir=repo_dir, filename="build.sh", **kwargs)
if status != SUCCESS:
sys.exit(status)

status = rerender_in_local_feedstock(pkg_name, workdir)
status = rerender_in_local_feedstock(**kwargs)

if args.do_build:
status = build_in_local_feedstock(pkg_name, workdir, args.build_version)
status = build_in_local_feedstock(**kwargs)

else:
# non conda-forge package (does not have feedstock)
repo_dir = os.path.join(workdir, repo_name)
print("Building non conda-forge package")
print("...branch: {b}".format(b=branch))
print("...build: {b}".format(b=build))
print("...repo_dir: {d}".format(d=repo_dir))

if args.do_rerender:
status = prepare_recipe_in_local_repo(branch, build, version, repo_dir)
status = prepare_recipe_in_local_repo(repo_dir=repo_dir, **kwargs)

if status != SUCCESS:
sys.exit(status)

status = rerender_in_local_repo(repo_dir)
# Create a fake feedstock in the workdir to run conda smithy in
feedstock_dir = create_fake_feedstock(repo_dir=repo_dir, **kwargs)

status = rerender_in_local_repo(repo_dir=feedstock_dir, **kwargs)
else:
feedstock_dir = os.path.join(workdir, "{}-feedstock".format(pkg_name))

if args.do_build:
status = build_in_local_repo(repo_dir, args.build_version)
status = build_in_local_repo(repo_dir=feedstock_dir, **kwargs)

sys.exit(status)

Expand Down
Loading