# ipm

ipm/ilm, an app to simplify project repository management.

Creating new files is tedious. Creating new projects is even more tedious.

ipm alleviates the tedious aspects of repository creation and maintenance by making it simple to create new files from templates and centralising your project configuration into a single file.

**Features**

- Create new projects from templates in seconds
- Create new files from templates in seconds
- Run scripts with support for templates and placeholders
- Simplify your workflow when creating new files and projects
- Rapidly iterate on your project with version bump and release support

Note: This project is released as "ilm" on npm.

# Getting started

ipm is written in JavaScript using Node and is distributed via npm.

On npm, ipm is distributed with the package name `ilm` to avoid a name collision with an existing package called `ipm`. This project may be renamed in the future to use `ilm` as its primary name.

First, install the project globally in your system:

`npm install -g ilm`

You will need a recent version of Node.

You can now launch ipm anywhere with the `ipm` or `ilm` command.

The first time that ipm is launched, it will create its [configuration](#configuration) file in your user directory.

You can now navigate to a project directory and initialise it with ipm: `ipm init`. Now ipm is ready to use in your project!

To find out what you can do, see the [API Reference](#api-reference) below!

## ipm updates

When an ipm update is released, run `npm install -g ipm@latest`.

You should pay attention to the changelog notes to ensure you correctly migrate your existing installation to the new release.

## ipm v0, v1 and v2

ipm v2 is the only supported version of ipm.

ipm v0 was written in Python and was fundamentally different to the ipm of today. There is no compatibility between ipm v0 and any later version; you should start from scratch with your `.ipm` file in projects when upgrading.

ipm v2 brings significant changes over v1, including the removal of some commands. You should not encounter any fundamental compatibility issues after installing v2 over a v1 installation, but be aware that some features have been removed or significantly changed. Your `.ipm` file in your projects (and your global `ipm.json` configuration file) *will* contain properties that are now redundant so it's recommended you start from scratch with these files when upgrading.

# App

The app object contains information about the application and its environment.

| Key | Description | Type | Default value |
| --- | ----------- | ---- | ------------- |
`app` | ipm version | string | -
`config` | ipm [configuration object](#configuration-object) containing the active configuration store from `~/.ipm/ipm.json` | object | -
`dir` | Working (project) directory path | string | -
`env` | ipm working environment (will always be `production` when in use as a released app) | string | `production`
`home` | Path to ipm configuration directory in user profile directory | string | -
`path` | Path to ipm executable installation directory | string | -
`project` | ipm [project object](#project-object) containing the active project store from `.ipm` in the working directory (`null` if project not initialised) | object | -
`route` | ipm [route object](#route-object) containing details about the command/route taken by the application | object | -

The app object is usually made available to [templates](#template-syntax) as the `app` variable.

Properties not documented here should be considered private and unsafe to use in templates.

## Route object

The route object contains details about the command/route taken by the application.

| Key | Description | Type | Default value |
| --- | ----------- | ---- | ------------- |
`applet` | Name of ipm applet being executed | string | -
`args` | Array of all arguments received on the command line for the applet being executed (i.e. command line arguments given by the user after the applet name) | array | -

You may use the information in this object inside [templates](#template-syntax), for example to interpolate the value of an argument given to the ipm command on the command line.

# Configuration

ipm stores your global user configuration in `~/.ipm/ipm.json`. The file is created as soon as you run ipm for the first time.

| Key | Description | Type | Default value |
| --- | ----------- | ---- | ------------- |
`scripts` | Script definitions to execute with the `run` command; these will be available globally in every project and in every directory, even when no ipm project has been initialised. Map of script name to shell command to execute. | object | -

You may add additional properties to the file and they will be available in [templates](#template-syntax) within the `config` object.

Properties not documented here should be considered private and unsafe to use in templates.

# Projects

Project files are stored in the root of your working directory as `.ipm`. They are created whenever you create a new project with the `init` command.

| Key | Description | Type | Default value |
| --- | ----------- | ---- | ------------- |
`name` | Project name | string | -
`title` | Project title | string | -
`author` | Author name | string | -
`version` | Version string | string | -
`copyright` | Copyright string | string | -
`vfiles` | Version file definitions for the [`bump`](#bump) command | array | -
`scripts` | Script definitions to execute with the `run` command. Map of script name to shell command to execute. | object | -

Many of these properties will be populated automatically when creating a new project with the `init` command.

The following getters are also defined, which can be used inside [templates](#template-syntax) to access additional dynamic values:

| Getter | Description | Type | Default value |
| ------ | ----------- | ---- | ------------- |
`versionMajor` | Project major version, from version string | int | -
`versionMinor` | Project minor version, from version string | int | -
`versionPatch` | Project patch version, from version string | int | -

You may add additional properties to the file and they will be available in [templates](#template-syntax) within the `project` object.

Properties not documented here should be considered private and unsafe to use in templates.

# Built-in file templates

ipm comes with a handful of built-in file templates to get you started; see `/templates` for details of what's included.

These can be used with the `touch` command without having to specify a file path – see the command documentation for more details – as ipm will automatically look for named templates in the built-in templates directory.

# Template syntax

ipm templates can be used in multiple places throughout the application, most notably in template files and within script definitions. See individual command documentations for more information. This section provides general information about using templates.

The basic syntax is as follows:

`{variable}`

Where `variable` is the name of the property to access.

This supports recursion into object properties:

`{some.object.property}`

Or getting an array value:

`{arr.0}`

You can also collect user input:

`{:input_here}`

If you use the same variable name in multiple places in a template, the value will only be collected once and will replace all instances in the template.

You can create templates of templates using the following placeholder escaping syntax:

`{[variable]}`

This will become `{variable}` in the rendered template: a valid placeholder when this rendered output is itself used as an ipm template input. This means you can use ipm as the basis of complex project scaffolding solutions, where you may create an abstract template that you then reuse across all your projects.

## Value sources

The sources for variable values depend on the command being used, but in general the following objects are available:

- `app` – ipm [app object](#app)
- `config` – [User configuration object](#configuration)
- `project` – Active ipm [project object](#project)

E.g. to access the name of the current project:

`{project.name}`

Or to access an argument passed on the CLI, for use in your template:

`{app.route.args.0}`

Unless a command documentation specifies otherwise, you should assume these are all the available variable sources for your templates.

# API Reference

This section lists all ipm commands and describes their functionality.

- [about](#about)
- [bump](#bump)
- [init](#init)
- [release](#release)
- [run](#run)
- [touch](#touch)

## about

*ipm*

ipm about applet which provides information about ipm.

## bump

*ipm bump {version}*

Bumps the project version to the semantic versioning string given by `{version}`. This must be specified **without** any leading "v".

Updates `.ipm` to reflect the new given version.

Also updates the version in the following files, if they are present (relative to the project working directory):

- `/version.txt` – replaces the file contents
- `/package.json` – updates `version` property
- `/package-lock.json` – updates `version` property

You can register additional replacement operations using the `vfiles` array property in your project definition file.

This must contain an array of objects with the following properties:

- `path` – path to the file to update, relative to your project directory root, with leading `/`
- `match` – match definition to identify text to replace in the file (used with JavaScript `replace(...)` method)
- `replace` – new text to replace each identified match with in the file

In `match` and `replace`, use the `{$}` placeholder to insert the new version which the project is being bumped to, and the `{$$}` placeholder to insert the previous/current project version.

Note: It is not currently possible to disable the default included match operations; therefore, if you want to override these, you should define your own matcher (yours are always run after the included ones), which should use the `{$}` placeholder in `match` (since ipm will have already updated the file with the incoming version, so `{$$}` will no longer match).

## init

*ipm init*

Initialise ipm in a new project working directory.

Populates the ipm project file and will prompt for input interactively for all required fields.

## release

*ipm release*

Create a release of your project based on the current version in the ipm file.

This tags a release on the Git `master` branch. You **must** make sure the `version` property in your `.ipm` file reflects the version you want to release.

There is currently no support for customising the script which is run during release. You can add additional commands by incorporating `ipm release` within one of your own ipm scripts (i.e. create your own `release` script and run `ipm release` within it).

For a project with version `0.1.0`, the commands executed in the project working directory will be:

```
"git checkout master",
"git tag -a 0.1.0 -m v0.1.0",
"git push",
"git push --tags",
"git status"
```

Note: You can check the exact commands to be run using `ipm release ~dry`, which will log all the commands without running anything.

## run

*ipm run {script}*

Run a named ipm script defined either in your project definition or in your user configuration (looks first in the project).

Scripts are defined as:

`{foobar: "echo foobar"}`

Running `ipm run foobar` will result in `echo foobar` being executed.

Scripts are always resolved as [ipm templates](#template-syntax).

You may also specify scripts as an array:

`{foobar: ["git status", "git checkout"]}`

This will result in `git status && git checkout` being run (array is always chained with `&&`). Again, the final command will be interpreted as an ipm template.

You can dry run a script by passing the `~dry` argument (i.e. `ipm run {script} ~dry`).

## touch

*ipm touch {template} {file}*

Creates a new file in the working directory, optionally from an [ipm template](#template-syntax).

`{file}` is a path to write relative to the project working directory. Note: if the file already exists, it will be overwritten.

ipm will try to find the template given as `{template}` by looking first relative to the project working directory, then the ipm user configuration directory, then the ipm [built-in templates](#built-in-file-templates) directory, then the root filesystem. If the template cannot be found, the output file will still be created but will be empty.

`{template}` can be a path to a directory, in which case *all* the files in the directory will be created in the project's working directory, with recursion into sub-directories. Each file will be interpreted as an ipm template. Templates which collect user input for placeholders will have that input persisted across all templates in the operation – i.e. if `file1.txt` and `file2.txt` both include a placeholder `{:input}`, ipm will only prompt for the value once, and both files will be populated with the same value.

The template directory can include a special file `ipm.json` in its root which, if it exists, will be merged with your project's `.ipm` file. The `ipm.json` is itself interpreted as an ipm template, so can contain placeholders. Any files in the template directory named `ipm.json` will not be created as output files in your working directory, irrespective of their nesting level (but only the top-level directory's `ipm.json` will be merged with `.ipm`).

If only one argument is given to the command, it will be interpreted as both `{file}` and `{template}`.

End of documentation.

©James Walker. Licensed under the MIT License.
