DevAssistant

Set up your development environment, publish your code, and do many of the chores that somehow keep you from the important stuff — writing your software.

Creating assistants for DevAssistant

This section is intended for developers that want to make their own assistants for their project. If you want to learn how to use DevAssistant, read Getting Started with DevAssistant. Furthermore, the tutorial is significantly condensed to be as clear and short as possible. For a much more detailed tutorial, visit DevAssistant documentation.

Why to create an assistant

If you contribute to a software project, especially one that many contributors work on, you can benefit greatly from having an assistant set suited precisely to your project. Likewise, if you like working with a language that is not yet covered by existing assistants, you can create a new assistant package that does just that.

While you can make your assitants without packaging and distributing them (more on that here), we strongly suggest you start creating the whole package (DAP—DevAssistant Package), and this tutorial takes that into consideration.

Installing the dap assistant

If you have DevAssistant installed (to check that, see the installation guide), see if you also have the DAP package for creating other DAPs. This command should return a line with information about an installed package named dap:

$ da pkg list --installed | grep dap

If the command returned nothing, either install the package through Fedora’s DNF:

$ sudo dnf install devassistant-dap-dap

or install the DAP locally for your account only:

$ da pkg install dap

Creating the package

With all the necessary bits and pieces in place, you can start with creating the package. Go to your working directory and create a new DAP with the desired assistants roles prepared (any ofcreate, tweak, prepare, extras):

$ da create dap --name foo --crt --prep

The above command creates a directory named dap-foo (dap- is added due to naming conventions), and in it a structure with assistants, in this case for the creation and preparation of projects. There are also directories for documentation, auxiliary files, and icons (useful for making your assistant accessible to GUI users).

dap-foo/
├── assistants/
│   ├── crt/
│   │   └── foo.yaml
│   └── prep/
│       └── foo.yaml
├── doc/
│   └── foo/
│       └── LICENSE
├── files/
│   └── foo/
├── icons/
│   └── foo/
└── meta.yaml

The file meta.yaml contains metadata about the whole package, the author, and dependencies. It is important you fill it in properly because otherwise you will not be able to package it or publish it on the DevAssistant Package Index (DAPI). Do not forget to include a licence of your choice too.

Writing sample assistant

An assistant is a YAML file with several sections that describe what it does. In this chapter, we will go through the typical ones. The full description can be found in the documentation. If you do not like the idea of writing a YAML assistant, you can use a language of your choice for most parts of the assistant (so far only Python support is implemented).

The header information contains data displayed to users when looking for the right assistant for their use case.

fullname: Assistant for Foo Bar Baz
description: More verbose description of what it does

Dependencies

The dependencies section describes what packages should be installed by which package manager. There are many package managers supported, to name a few: Yum, DNF, gem, pip, pacman, and more. To indicate that e. g. RPM packages baz and qux, and a Python (pip) package foobar should be installed, form the dependencies section the following way:

dependencies:
  - rpm: ['baz', 'qux']
  - pip: ['foobar']

Arguments

To allow the user to provide input, arguments are needed. Either you specify the argument properties yourself (in this case, a mandatory argument named name specified by flags -n and --name):

args:
  name:
    flags: [-n, --name]
    required: True
    help: A short description of the argument

or you can use a definition from a so-called snippet (think of it a header file or a library), in this case a snippet named common_args:

args:
  name:
    use: common_args

If you use a defintion from a snippet, you must declare a dependency on it in the meta.yaml file in your package (by adding a dependencies: ['common_args'] to the file.

Arguments’ values are automatically stored in a variable of the same name, in this case, $name.

Files

You assistant may include other files than just the assistants, for example those that will be placed in the project’s directory on creation. To include a file named foo.txt, copy it into the files subdirectory in the DAP’s structure, and add a reference to it to the files section in the assistant under an appropriate handle, in this case e. g. foo:

files:
  foo: &foo
    source: foo.txt

Elsewhere in the assistant, the file will be only referred to by its handle foo. Repeating the handle name with & is necessary due to technical limitations of the current implementation.

Run section

The run section is a procedural section where you put the commands that should be executed during the assistant’s run. Typically, you create a directory to put the project into, copy the files from the files section, start services, emit messages to be logged, or push the code to GitHub. For the full list of possible commands, have a look at run section reference.

The run section consists of a list of commands (each item of the list starting with -), their arguments, and other logic. To illustrate this better, let us have a look at this sample run section:

run:
- setup_project_dir:
    from: name
    on_existing: fail
- cl: cd $contdir/$topdir
- $date~: $(date)
- log_i: Run started at $date
- cl: cp *foo .
- if $(test -f foo.txt):
    - log_i: Foo.txt copied successfully
- else:
    - log_e: Error copying Foo.txt

As fairly obvious, the command setup_project_dir creates the project dir from the variable name, as specified, and will fail if the directory already exists. What is not obvious is that in addition to that, it sets the variables $contdir (the directory one level above the project directory) and $topdir (the project directory), which will be used shortly.

The next command, cl: cd $contdir/$topdir, executes a shell command that changes the current working directory to the project’s directory. If you want to simply execute calls in the shell, call the cl (command line) command. If you want to assign the result of the call to a variable or check the result in an if clause, use the $(...) syntax like in shell instead, as shown on the next line. The line in question, $date~: $(date), is a variable assignment. In this case, it assigns the result of calling the shell command date into a variable named $date. The tilde (~) is there so that $(date) is interpreted. If it was not there, the assigned value would be the string $(date) itself. Generally speaking, all commands can be appended with the tilde to force interpreting the value on the right side of the colon.

The commands log_i, log_w, and log_e are all used for logging on different levels (INFO, WARNING, ERROR). Unlike the first two, log_e also stops the execution of the assistant as it means fatal error that DevAssistant can not recover from.

The last thing not yet covered in this little example is the if..else construct. It works just how you expect it to, except not only can you query shell command results (test -f foo.txt) in the example, but also the values of variables (if defined $variable, for example) and more.

The whole assistant

The sections that were described above add up to a simple little assistant that looks like this:

fullname: Assistant for Foo Bar Baz
description: More verbose description of what it does

dependencies:
  - rpm: ['baz', 'qux']
  - pip: ['foobar']

args:
  name:
    use: common_args

files:
  foo: &foo
    source: foo.txt

run:
- setup_project_dir:
    from: name
    on_existing: fail
- cl: cd $contdir/$topdir
- $date~: $(date)
- log_i: Run started at $date
- cl: cp *foo .
- if $(test -f foo.txt):
    - log_i: Foo.txt copied successfully
- else:
    - log_e: Error copying Foo.txt

Please, bear in mind that this is a small and non-exhaustive example of what can be done with DevAssistant. To see the whole set of options and a more complicated example, visit the Create Your Own Assistant section of the DevAssistant Documentation.

Packaging

When the assistants are created, you can prepare the whole package for distribution, which is done by executing:

$ da tweak dap pack

in the source directory. This command creates a properly formatted DAP from the files present, and saves it under the name dap-NAME-VERSION.dap. To ensure the DAP meets all the requirements for distribution, run:

$ da pkg lint ./dap-NAME-VERSION.dap

The command outputs a list of problems that prevent the DAP from being used in distribution. After the problems are sorted out, you can install the DAP by running:

$ da pkg install ./dap-NAME-VERSION.dap

or upload it to the DAPI for others to consume. Use the command:

$ da extra dap upload -d ./dap-NAME-VERSION.dap