jobless

package module
v0.0.0-...-7af3aca Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2014 License: MIT Imports: 8 Imported by: 1

README

Jobless

Jobless is a silly name for an experiment in reorganizing how the entrypoints to "verbs" in a project can be organised (ie. "build", "test", "run")

There are a few ideas under test here:

  • Firstly, the idea that entrypoints are best organised by being allowed to be kept next to the code for them wherever that is in the project, but exposed for discovery and use from a top level.
  • That they benefit from being able to share variables based on the project structure: Things like GOPATH, or branch root folder, shared without repetition to all files below.
  • That expressing relative paths consistently based on the path relative to the file are expressed in is cognitively easier.
  • That you rarely want the CWD to be inherited by project entrypoints

In the end, you should not have to express "../../../" in many cases. You should be able to give a user the ability to run all tests, from potentially multiple programming languages and multiple sub-projects, without having to make a batch script that calls down to each of them.

I use YAML for a few reasons:

  • It's a lot more readable and shorter than JSON
  • You can comment in YAML, which makes for better configuration files

So here's an example of jobless in use to build itself:

I put this file in my root workspace.

Variables:
    GOPATH: "{{ path `.` }}"
    OUTPUT_FOLDER: "{{ path `.` }}"

Here is an example of using those variables further down the tree:

Tasks:
    - Name: jobless.test
      # Automatically parsing the command and arguments from a string would be nicer
      Command: ["go", "test", "-v", "."]
      Environment:
            # This is using go's text template system for convenience
            # A more readable mechanism would be better
            GOPATH: "{{var `GOPATH`}}"  
    # Because this is YAML, you can comment
    # This command should be translated to run cmd to execute the batch script          
    - Name: jobless.batch.test1
      Command: ["test.bat"]
    # This one does not need translation
    - Name: jobless.batch.test2
      Command: ["cmd", "/c", "test.bat"]

At the moment, jobless doesn't look above the cwd for files, but it might be a nice enhancement. Notice that the file is relatively readable, commented and tasks are (by definition) locked down to a single command. You don't need multiple files to define multiple entrypoints.

Now we can discover these tasks from anywhere above them in the filesystem using:

jobless find
> jobless.build in E:\stuff\GoFiddle\src\github.com\Redundancy\jobless\cmd\jobless.jobless
> jobless.test in E:\stuff\GoFiddle\src\github.com\Redundancy\jobless\lib.jobless
> jobless.batch.test1 in E:\stuff\GoFiddle\src\github.com\Redundancy\jobless\lib.jobless
> jobless.batch.test2 in E:\stuff\GoFiddle\src\github.com\Redundancy\jobless\lib.jobless

and we can execute them using a few methods:

  • jobless run ** - run everything in sequence
  • jobless run jobless.build - run just jobless.build
  • jobless run jobless.* - run jobless.build and jobless.test
  • jobless run jobless.** - run everything in the jobless namespace
  • jobless run **.test - run everything ending in test

(you hopefully get the idea)

Note that 'find' also takes these arguments.

Why?

Batch files are terrible.

  • Sharing variables largely has to be done by setting them in other things you call
  • Unset variables often require many line batch tests using awful notation
  • As with go projects that you go get, you don't always have access to put he helpful batch scripts at the root
  • It's nicer to put entrypoints next to the code that they're supposed to run
  • You want to be able to find all the things someone thinks you can "do" with a project
  • I've often wanted to "run all the tests" on a branch of a large project, but found it difficult to get to them all and run them
  • Be able to list out, debug the values of, and check for the presence of any variables easily
Further ideas
  • Tasks that run other tasks in sequence (test, then build)
  • Hidden tasks (tasks that are only supposed to be run as part of a sequence)
  • Encrypted variables (consolidate your settings in VCS securely)
  • Overrides - set an overide for a variable in jobless
  • Lookups - do a lookup for a variable value based on something else
Go crazy

Once you've got things running commands in sequence, with easy configuration, how far away are you from having a much nicer way of doing a CI system?

Why do CI systems NOT get their configuration from source control? it allows for much better integration behaviour between branches, and if you're using the same system for your entrypoints, you're already building something at a nice level of granularity that's not just for one use.

Documentation

Index

Constants

View Source
const (
	QUOTE           = `"`
	SEPARATOR       = `.`
	MATCH_ANYTHING  = `*`
	MATCH_RECURSIVE = `**`
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ChainedVariableStore

type ChainedVariableStore struct {
	Parent    *ChainedVariableStore
	Path      string
	Variables map[string]string
}

func (*ChainedVariableStore) Resolve

func (c *ChainedVariableStore) Resolve(variable string) (string, error)

func (*ChainedVariableStore) ResolveString

func (c *ChainedVariableStore) ResolveString(value string) (string, error)

type ResolveErrors

type ResolveErrors []error

func (ResolveErrors) Error

func (r ResolveErrors) Error() string

type TaskName

type TaskName []string

func TaskNameFromString

func TaskNameFromString(s string) TaskName

func (TaskName) GetYAML

func (t TaskName) GetYAML() (tag string, value interface{})

YAML saving support

func (TaskName) IsAncestorOf

func (t TaskName) IsAncestorOf(o TaskName) bool

func (TaskName) IsChildOf

func (t TaskName) IsChildOf(o TaskName) bool

func (TaskName) IsParentOf

func (t TaskName) IsParentOf(o TaskName) bool

Returns true, if this TaskName is the direct parent of o

func (TaskName) IsRoot

func (t TaskName) IsRoot() bool

func (TaskName) MarshalJSON

func (t TaskName) MarshalJSON() ([]byte, error)

JSON saving support

func (TaskName) Matches

func (t TaskName) Matches(pattern string) bool

Returns true if this task matches a pattern in another task * is a wildcard, ** is a recursive wildcard for example:

*.*.*test matches A.B.unittest
**.unittest matches it too

func (TaskName) Parent

func (t TaskName) Parent() TaskName

func (*TaskName) SetYAML

func (t *TaskName) SetYAML(tag string, value interface{}) bool

Yaml loading support

func (TaskName) String

func (t TaskName) String() string

func (*TaskName) UnmarshalJSON

func (t *TaskName) UnmarshalJSON(d []byte) error

JSON loading suppor

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL