npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@optask/tasker

v1.2.1

Published

A task scheduler and runner for deployments, developed in nodejs

Downloads

638

Readme

@optask/tasker

Tasker is a simple task runner designed to be useful when running deployment jobs. If you need to only run tasks under certain conditions, or only run a task once, you can hook up those conditions to the tasks you are running and schedule them to run on deployment, then watch as only the things you specified run.

Quickstart

Install tasker:

yarn add @optask/tasker

Inside your project create a new task:

import { BaseTask, EqualsCondition } from '@optask/tasker';

export class MigrateTask extends BaseTask {
    constructor() {
        super({
            name: 'Migrate', // All tasks must have a unique name
            condition: new EqualsCondition('version', '1.2.0'), // Only runs when the 'version' condition is equal to the string '1.2.0'
        });
    }

    override async run(args: string[]) {
        const db = someDBConnection(args);
        await db.migrate();
    }
}

Then run it with a scheduler:

import { TaskScheduler } from '@optask/tasker';
import { MigrateTask } from 'MigrateTask';

const scheduler = new TaskScheduler();

// Add the tasks to run to the task scheduler
scheduler.registerTask(new MigrateTask());

// Tell the scheduler about the conditions it is running under
scheduler.addCondition(`version`, process.env.VERSION);

// Finally run the tasks with the scheduler
await scheduler.run();

Tasks

Creating tasks is as simple as extending the Task interface. Tasker is configured around class based tasks, but they can be any object as long as they extend that interface.

However, the recommended approach is to create a new class for each task you need, and simply extend BaseTask which has basic configuration already.

export class MyTask extends BaseTask {
    constructor(){
        super({
            name: 'deploy' // All tasks must have a unique name
        })
    }

    // All tasks must have  run method, can be async
    override async run(args: string[]) {
        // Do things
    }
}

Tasks can be configured in a number of ways to fit almost any need:

  • Dependencies: If you pass a dependsOn array of other task names, the scheduler will assure that those tasks are scheduled to run before this task.
  • Conditions: All tasks must have a Condition which can be a test of if the task should run or not. See conditions below. If no condition is set, the BaseTask will default to the AnyCondition which allows the task to run every time (under any conditions).
  • Modifiers: Modifiers are special bits of code that can manage if tasks are skipped based on some external criteria. The most common modifier is the RunOnceModifier which modifies the execution of the task to only run one time, using a storage mechanism to track the previous run's state. See modifiers below.

Here is an example of a task that only runs once for each version of the app, depends on a separate task, and only runs when the 'step' of deployment is 'pre-deploy':

import { BaseTask, EqualsCondition, RunOnceModifier } from '@optask/tasker';

export class MigrateTask extends BaseTask {
    constructor() {
        super({
            name: 'Migrate', // Our unique task name
            dependsOn: ['PauseTraffic'], // The "PauseTraffic" task must run first
            condition: new EqualsCondition('step', 'pre-deploy'), // We only run this task when 'step' is 'pre-deploy'
            modifiers: [new RunOnceModifier(['version'])], // We only run this one time for each 'version' of the app
        })
    }

    override async run(args: string[]) {
        // Do things here
    }
}

Task Conditions

A condition is simply a test of if a task should run. When setting up the task scheduler, you must provide the conditions of the current deployment or scheduler run, often from environment variables or some external store.

scheduler.addCondition('version', '1.2.3'); // Example of adding a condition

Alternatively you can provide conditions from the registered store:

scheduler.setShouldLookupConditionsInStore(true);
await scheduler.getStore().set('version', '1.2.3');

The base implementation of a task specifies its condition as AnyCondition by default, meaning it will always run.

Note that every task has exactly one condition attached to it under which it is evaluated by the scheduler before running. If your conditions become more complex, you can use "compound" conditions instead. Three basic compound conditions are provided for you:

  • NOTCondition takes in a condition and inverts it's output
  • ANDCondition will result in the logical 'and' of an array of input conditions
  • ORCondition will result in the logical 'or' of an array of input conditions

Note that with these building blocks you should be able to construct any other complex condition. tasker provides a set of simple conditions as well:

  • EqualsCondition asserts that the condition equals some value
  • LessThanCondition asserts that the condition (a number or Date) is less than some value
  • GreaterThanCondition asserts that the condition (a number or Date) is greater than some value

There is also a GeneralCondition which takes in a callback so you can create whatever condition parameters you need.

For example, you could test if a character/substring appears in a string:

const hasCharacterCondition = (name: string, value: string) => {
    return new GeneralCondition(name, (nameValue: string) => nameValue.includes(value));
};

const someConditionHasAnS = hasCharacterCondition('someConditionVariable', 's');

To create your own condition, you can use general condition or implement the Condition interface.

Here is an example of how the simple EqualsCondition is implemented:

export class EqualsCondition<V extends string | number | boolean>
  implements Condition<V>
{
  value: V;
  name: string;

  constructor(_name: string, _value: V) {
    this.value = _value;
    this.name = _name;
  }

  comparison(condition: V) {
    return condition === this.value;
  }
}

Testing your conditions

The scheduler implements a method conditionsApplyTo which takes a task and will test its condition against the scheduler's condition state.

const myCondition = new GeneralCondition('test', () => );//something
scheduler.addCondition('test', 'value');

const task = new MyTask();
task.condition = myCondition;

const pass = scheduler.conditionsApplyTo(task);

Task Modifiers

Another way to modify how tasks will run is to attach modifiers to them. Tasks have an array of modifiers that implement the Modifier interface.

A modifier can implement 3 methods to attach to the lifecycle of a task:

  • shouldTaskRun Will provide the modifier an opportunity to skip the task based on some test it does
  • taskWillRun Called before the task runs
  • taskDidRun Called after the task completes (or if the task throws)

All modifier methods take in some data about the current task, store, conditions, etc.

A common modifier provided by tasker is the RunOnceModifier which stores when tasks succeed in the provided store and will only run them one time (the task is skipped if it succeeded earlier according to the store).

The modifier takes in a list of conditions that it should re-run under, aka if you have a version condition, you can make the task run once per version by providing version condition to the modifier:

task.modifiers.push(new RunOnceModifier([`version`]));