Skip to content

Commit

Permalink
Merge pull request #25 from josegonzalez/master
Browse files Browse the repository at this point in the history
Release 0.10.0
  • Loading branch information
josegonzalez authored Oct 26, 2020
2 parents 1f6dbe4 + 3a169bc commit f275569
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 44 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ MAINTAINER_NAME = Jose Diaz-Gonzalez
REPOSITORY = go-procfile-util
HARDWARE = $(shell uname -m)
SYSTEM_NAME = $(shell uname -s | tr '[:upper:]' '[:lower:]')
BASE_VERSION ?= 0.9.1
BASE_VERSION ?= 0.10.0
IMAGE_NAME ?= $(MAINTAINER)/$(REPOSITORY)
PACKAGECLOUD_REPOSITORY ?= dokku/dokku-betafish

Expand Down
48 changes: 48 additions & 0 deletions PROCFILE_FORMAT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Procfile

A Procfile is a file that was [promoted by Heroku](https://blog.heroku.com/the_new_heroku_1_process_model_procfile) for their platform as an easy way to specify one or more distinct processes to run within Heroku. This format has since been picked up by various other tools and platforms.

## General Overview

The `procfile-util` tool expects a Procfile to be defined as one or more lines containing one of:

- a comment (preceeded by a `#` symbol)
- a process-type/command combination (with optional trailing whitespace or trailing comment)
- when there is a trailing comment, the `#` symbol _must_ be preceeded by one or more `whitespace` characters.
- a blank line (with optional trailing whitespace)

Comments and blank lines are ignored, while process-type/command combinations look like the following:

```
<process type>: <command>
```

The syntax is defined as follows:

- `<process type>` – any character in the class `[A-Za-z0-9_-]+`, a process type is a name for your command, such as `web`, `worker`, `urgentworker`, `clock`, etc.
- `<command>` – a command used to launch the process, such as `rake jobs:work`

Additional rules are as follows:

- contain at most 63 characters

A Procfile that does not match any of the above rules will be considered invalid, and not be processed. This is to avoid issues where a Procfile may contain merge conflicts or other improper content, thus resulting in unwanted runtime behavior for applications.

Finally, process types within a Procfile may not overlap and must be unique. Rather than assuming that the last or first specified is correct, `procfile-util` will fail to parse the Procfile with the relevant error.

### Strict Mode

> Strict mode can be triggered on `go-procfile-util` via the `--strict` flag.
In strict mode, the character set of a process type changes.

- `<process type>` – a valid DNS Label Name as per [RFC 1123](https://tools.ietf.org/html/rfc1123), a process type is a name for your command, such as `web`, `worker`, `urgentworker`, `clock`, etc.

This syntax differs common interpretations of valid `<process type>` values in that we define the process type as a DNS Label name, versus the regex `[A-Za-z0-9_]+`. The reason for this is that processes defined within Procfiles are commonly used in DNS entries. Rather than have a second level of platform-specific validation in place, this project implicitly defines the format for the process-type.

Given the above, a valid process type can be generalized to the following rules (as taken from the [Kubernetes documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names)):

- contain at most 63 characters
- contain only lowercase alphanumeric characters or '-'
- start with an alphanumeric character
- end with an alphanumeric character
33 changes: 1 addition & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,7 @@ Install it using the "go get" command:

## What is a Procfile

A Procfile is a file that was [promoted by Heroku](https://blog.heroku.com/the_new_heroku_1_process_model_procfile) for their platform as an easy way to specify one or more distinct processes to run within Heroku. This format has since been picked up by various other tools and platforms.

The `procfile-util` tool expects a Procfile to be defined as one or more lines containing one of:

- a comment (preceeded by a `#` symbol)
- a process-type/command combination (with optional trailing whitespace or trailing comment)
- when there is a trailing comment, the `#` symbol _must_ be preceeded by one or more `whitespace` characters.
- a blank line (with optional trailing whitespace)

Comments and blank lines are ignored, while process-type/command combinations look like the following:

```
<process type>: <command>
```

The syntax is defined as follows:

- `<process type>` – a valid DNS Label Name as per [RFC 1123](https://tools.ietf.org/html/rfc1123), a process type is a name for your command, such as `web`, `worker`, `urgentworker`, `clock`, etc.
- `<command>` – a command used to launch the process, such as `rake jobs:work`

This syntax differs common interpretations of validd `<process type>` values in that we define the process type as a DNS Label name, versus the regex `[A-Za-z0-9_]+`. The reason for this is that processes defined within Procfiles are commonly used in DNS entries. Rather than have a second level of platform-specific validation in place, this project implicitly defines the format for the process-type.

Given the above, a valid process type can be generalized to the following rules (as taken from the [Kubernetes documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names)):

- contain at most 63 characters
- contain only lowercase alphanumeric characters or '-'
- start with an alphanumeric character
- end with an alphanumeric character

Additionally, should a Procfile contain a line that does not match one of the three patterns mentioned above, the entire Procfile is considered invalid, and will not be processed. This is to avoid issues where a Procfile may contain merge conflicts or other improper content, thus resulting in unwanted runtime behavior for applications.

Finally, process types within a Procfile may not overlap and must be unique. Rather than assuming that the last or first specified is correct, `procfile-util` will fail to parse the Procfile with the relevant error.
See [PROCFILE_FORMAT.md](/PROCFILE_FORMAT.md) for more details on the general format.

## Usage

Expand Down
36 changes: 25 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,23 @@ func writeProcfile(path string, delimiter string, entries []procfileEntry) error
return w.Flush()
}

func parseProcfile(path string, delimiter string) ([]procfileEntry, error) {
func parseProcfile(path string, delimiter string, strict bool) ([]procfileEntry, error) {
var entries []procfileEntry
reCmd, _ := regexp.Compile(`^([a-z0-9]([-a-z0-9]*[a-z0-9])?)` + delimiter + `\s*(.+)$`)
reOldCmd, _ := regexp.Compile(`^([A-Za-z0-9_-]+)` + delimiter + `\s*(.+)$`)

reComment, _ := regexp.Compile(`^(.*)\s#.+$`)

text, err := getProcfile(path)
if err != nil {
return entries, err
}

return ParseProcfile(text, delimiter, strict)
}

func ParseProcfile(text string, delimiter string, strict bool) ([]procfileEntry, error) {
var entries []procfileEntry
reCmd, _ := regexp.Compile(`^([a-z0-9]([-a-z0-9]*[a-z0-9])?)` + delimiter + `\s*(.+)$`)
reOldCmd, _ := regexp.Compile(`^([A-Za-z0-9_-]+)` + delimiter + `\s*(.+)$`)

reComment, _ := regexp.Compile(`^(.*)\s#.+$`)

lineNumber := 0
names := make(map[string]bool)
scanner := bufio.NewScanner(strings.NewReader(text))
Expand All @@ -175,14 +180,23 @@ func parseProcfile(path string, delimiter string) ([]procfileEntry, error) {
}

if !isCommand {
if isOldCommand {
if !isOldCommand {
return entries, fmt.Errorf("invalid line in procfile, line %d", lineNumber)
}

if strict {
return entries, fmt.Errorf("process name contains invalid characters, line %d", lineNumber)
}
}

return entries, fmt.Errorf("invalid line in procfile, line %d", lineNumber)
name := ""
cmd := ""
if strict {
name, cmd = params[1], params[3]
} else {
name, cmd = oldParams[1], oldParams[2]
}

name, cmd := params[1], params[3]
if len(name) > 63 {
return entries, fmt.Errorf("process name over 63 characters, line %d", lineNumber)
}
Expand Down Expand Up @@ -550,6 +564,7 @@ func main() {
procfileFlag := parser.String("P", "procfile", &argparse.Options{Default: "Procfile", Help: "path to a procfile"})
delimiterFlag := parser.String("D", "delimiter", &argparse.Options{Default: ":", Help: "delimiter in use within procfile"})
defaultPortFlag := parser.String("d", "default-port", &argparse.Options{Default: "5000", Help: "default port to use"})
strictFlag := parser.Flag("S", "strict", &argparse.Options{Help: "strictly parse the Procfile"})
versionFlag := parser.Flag("v", "version", &argparse.Options{Help: "show version"})

checkCmd := parser.NewCommand("check", "check that the specified procfile is valid")
Expand Down Expand Up @@ -619,8 +634,7 @@ func main() {
}

Loglevel = *loglevelFlag

entries, err := parseProcfile(*procfileFlag, *delimiterFlag)
entries, err := parseProcfile(*procfileFlag, *delimiterFlag, *strictFlag)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
Expand Down

0 comments on commit f275569

Please sign in to comment.