0.0.0-rc.7 • Published 3 years ago

@instaci/run v0.0.0-rc.7

Weekly downloads
254
License
-
Repository
-
Last release
3 years ago

Task Runner

⚡️ The Task Runner allows to define, and run tasks locally or remotely.

A task is just defined by calling the function task(name, callback, options?), for example:

import { task, logger } from './api';

task('First task', async () => {
  logger.info('Hello world!');
});

Tasks can express dependencies:

import { task, logger } from './api';

const firstTask = task('First task', async () => {
  logger.info('Hello world 1!');
});

task('Second task', async () => {
  logger.info('Hello world 2!');
}, {
  dependsOn: [ firstTask ],
});

In the above example, Second task runs after First task.

By default, tasks run in parallel when possible, but setting dependencies changes the order in which the tasks are executed.

Nesting Tasks

A task can contain another tasks. In this case, a task succeeds when all their child task also succeed.

import { task, logger } from './api';

task('First task', async () => {

  task('Second task', async () => {
    logger.info('Hello world 2!');
  });
  
  logger.info('Hello world 1!');
});

In the above example, Hello world 1! is printed first, then Hello world 2!.

Rendering UIs

A task can generate a GUI to allow users to manipulate the output of a task, re-trigger additonal tasks, and modify the files in the repository.

For example:

import React from 'react';

import { action, task } from './api';

const acceptOutput = task('Accept Generated Output', async () => {
  log.info('The user just accepted the output');
});

function ConfirmOutput() {
  const output = '<read-file-content>';
  return (
    <>
      <textarea>{output}</textarea>
      <button onClick={acceptOutput}>Accept</button>
    </>
  );
}

task('First task', async () => {

  action('Confirm output', () => <ConfirmOutput />);

});

The above example runs a task named First task.

The action Confirm output is generated which then asks the user to confirm some generated output. It does this by rendering a <textarea> and a <button> in the web browser. Once the user clicks the "Accept" button, a new task quicks off.

Under the hood, all the code in a task runs remotely, everything else runs in the web browser. As a result, the browser side can read attachments and state shared from the enclosing task, or its task dependencies.

Sharing Tasks

You can create higher order functions that take some configuration, and export a task.

import { task, logger } from './api';

// Shared library
function CustomTask(name: string, config?: { message?: string }) {
  return task(name, async () => {
    logger.info(config?.message);
  });
}

// User code
CustomTask('First task', {
  message: 'Hello world',
});

Helper Functions

These are defined in api.ts.

  • cd(dir: string): Changes the current directory.
  • exec(command: string, arguments: string[]): Executes a program within the current directory, and streams the stdout, and stderr. The enclosing task fails if the program exited with error.
  • sleep(timeMs: number): Sleeps for timeMs milliseconds.
  • logger.info(message: any): Logs an info message.
  • logger.error(message: any): Logs an error message.

Configuring Tasks

Tasks shall be defined statically. That is, no task should be configured based on values defined at runtime.

This ensures that the task DAG is deterministic, and reproducible.

Development

Run bootstrap.ts to download a repository from GitHub, and run the tasks defined in the .insta/ directory:

INSTA_REPOSITORY='<repository>' \
INSTA_REPOSITORY_ACCESS_TOKEN='<access-token>' \
INSTA_REPOSITORY_BRANCH='<repository-branch>' \
INSTA_JOB_ID=1 \
INSTA_COMMIT_ID=1 \
INSTA_PROJECT_ID=1 \
INSTA_RUNNER_ADDRESS=':7000' \
bazel run //:bin