Compare commits

...

90 Commits

Author SHA1 Message Date
Jose Diaz-Gonzalez
6cb8b108db Release 0.9.4
# History

## 0.9.4

### Documentation

- #2710: @josegonzalez Quiet output for git-related commands
2017-04-10 18:36:04 -06:00
Jose Diaz-Gonzalez
9aef43d5a0 Merge pull request #2710 from dokku/quiet-git
Quiet output for git-related commands
2017-04-10 18:35:25 -06:00
Jose Diaz-Gonzalez
8dadc4cf50 fix: de-dent post-extract output 2017-04-10 17:12:46 -06:00
Jose Diaz-Gonzalez
45518fce6c fix: ensure git check-ref-format runs quietly 2017-04-10 17:12:32 -06:00
Jose Diaz-Gonzalez
b21f4b6470 Release 0.9.3
# History

## 0.9.3

### Bug Fixes

- #2706: @josegonzalez fix: ensure nginx conf.d directory exists when running nginx install hook
- #2701: @scjody Set SSH_USER for root commands

### New Features

- #2708: @josegonzalez Document that we will not do buildpack support in the issue tracker

### Documentation

- #2709: @michaelshobbs increase CURL_TIMEOUT and CURL_CONNECT_TIMEOUT defaults
- #2699: @mbreit Add support for git >= 2.11
2017-04-10 13:40:53 -06:00
Jose Diaz-Gonzalez
c52f60f9d7 Merge pull request #2709 from dokku/mh/fix/default-curl-timeouts
increase CURL_TIMEOUT and CURL_CONNECT_TIMEOUT defaults
2017-04-10 13:39:42 -06:00
Jose Diaz-Gonzalez
c815d732eb Merge pull request #2706 from dokku/2686-missing-nginx-dir
fix: ensure nginx conf.d directory exists when running nginx install hook
2017-04-10 12:48:25 -06:00
Michael Hobbs
18849c085a increase CURL_TIMEOUT and CURL_CONNECT_TIMEOUT defaults. fixes #2668 2017-04-10 11:34:09 -07:00
Jose Diaz-Gonzalez
cb62958095 Merge pull request #2708 from dokku/josegonzalez-patch-1
Document that we will not do buildpack support in the issue tracker
2017-04-10 07:46:49 -06:00
Jose Diaz-Gonzalez
4fcfad5df5 docs: Document that we will not do buildpack support in the issue tracker
[ci skip]
2017-04-10 07:46:23 -06:00
Jose Diaz-Gonzalez
5101096518 fix: ensure nginx conf.d directory exists when running nginx install hook
Closes #2686
2017-04-10 00:37:23 -06:00
Jose Diaz-Gonzalez
afef3b94d5 Merge pull request #2701 from plotly/set-ssh-user-for-root
Set SSH_USER for root commands
2017-04-10 00:28:50 -06:00
Jody McIntyre
73a84f90c5 Set SSH_USER for root commands
Previously, SSH_USER was not set for plugin:* or ssh-keys:add, which
contradicts the user-auth trigger documentation.

Fixes #2700
2017-04-07 12:46:09 -04:00
Jose Diaz-Gonzalez
918bc9efe8 Merge pull request #2699 from mbreit/git-worktree
Use git worktree in git_build_app_repo() for git >= 2.11
2017-04-05 11:03:10 -04:00
Moritz Breit
98619733b2 Use git worktree in git_build_app_repo() for git >= 2.11
Git 2.11.0 breaks the git clone approach in git_build_app_repo() in
25ab004c53

git worktree is a much simpler approach but only works for git >= 2.5
so it is not available in Ubuntu 14.04.

Fixes #2512.
2017-04-05 11:45:41 +02:00
Jose Diaz-Gonzalez
2fb712c3a2 Release 0.9.2
# History

## 0.9.2

### New Features

- #2698: @josegonzalez docs: Document that we only care about specific sections
- #2697: @callahad Restore installer note regarding AUFS on Linode
- #2694: @scjody Add documentation note re: git-pre-pull vs. auth

### Documentation

- #2695: @michaelshobbs add tests for pre/post deploy tasks
2017-04-05 00:21:24 -04:00
Jose Diaz-Gonzalez
0fa6503034 Merge pull request #2698 from dokku/josegonzalez-patch-1
docs: Document that we only care about specific sections
2017-04-04 17:05:32 -04:00
Jose Diaz-Gonzalez
fab97a377f docs: Document that we only care about specific sections
Also remove all extra sections that are unused to make this clearer.

[ci skip]
2017-04-04 16:53:15 -04:00
Jose Diaz-Gonzalez
811bf2f9c4 Merge pull request #2697 from callahad/warn-about-aufs-warning
Restore installer note regarding AUFS on Linode
2017-04-04 15:37:38 -04:00
Dan Callahan
20497dabb4 Restore installer note regarding AUFS on Linode
See discussion in: https://github.com/dokku/dokku/issues/2675
Upstream Docker bug: https://github.com/docker/docker/issues/32361
2017-04-04 14:35:09 -05:00
Jose Diaz-Gonzalez
0b430af077 Merge pull request #2694 from plotly/git-pre-pull-security-note
Add documentation note re: git-pre-pull vs. auth
2017-04-04 12:50:37 -04:00
Jody McIntyre
e42e7c3694 Add documentation note re: git-pre-pull vs. auth 2017-04-04 11:40:46 -04:00
Michael Hobbs
eeef120433 Merge pull request #2695 from dokku/mh/chore/deployment-tasks-tests
add tests for pre/post deploy tasks
2017-04-03 16:32:36 -07:00
Michael Hobbs
8950571acb add tests for pre/post deploy tasks 2017-04-03 12:34:41 -07:00
Jose Diaz-Gonzalez
a72a82e29b Release 0.9.1
# History

## 0.9.1

### Bug Fixes

- #2693: @josegonzalez fix: explicitly chown data and data/storage directories
2017-04-03 00:17:02 -04:00
Jose Diaz-Gonzalez
b6adc23bdd Merge pull request #2693 from dokku/josegonzalez-patch-1
fix: explicitly chown data and data/storage directories
2017-04-02 16:17:41 -04:00
Jose Diaz-Gonzalez
8677aadad7 fix: explicitly chown data and data/storage directories
Doing a recursive chown will otherwise screw up dockerfile-based app storage when the uid does not map to herokuish or similar.
2017-04-02 15:07:18 -04:00
Jose Diaz-Gonzalez
a4b8e59c3f Release 0.9.0
# History

## 0.9.0

### Bug Fixes

- #2691: @josegonzalez Fix package building when golang binaries are available
- #2671: @znz Fix variable name
- #2672: @callahad Fix logrotate on Debian
- #2666: @josegonzalez Use correct flag for build arguments when installing herokuish
- #2664: @pvalentim Fix remote name when using --remote option with apps:create

### New Features

- #2689: @mbreit Add dokku-monit to community plugin list
- #2683: @josegonzalez Ensure we have an example for adding keys as another user
- #2682: @josegonzalez Clarify supported stanzas in app.json
- #2679: @callahad Remove unnecessary Linode-specific instructions
- #2670: @znz Remove duplicated `(i.e. `

### Documentation

- #2685: @josegonzalez Pass shellcheck on os x
- #2677: @callahad Prefer HTTP2 to SPDY in nginx-vhosts
- #2673: @michaelshobbs Update to herokuish 0.3.27
- #2674: @michaelshobbs Update sshcommand to 0.7.0
- #2654: @ebeigarts Enable nginx and docker on system startup when using bootstrap.sh on CentOS
- #2546: @michaelshobbs Convert repo plugin to golang
2017-03-31 18:33:39 -06:00
Jose Diaz-Gonzalez
3a5d0716d4 Merge pull request #2691 from dokku/packaging
Fix package building when golang binaries are available
2017-03-31 18:12:44 -06:00
Jose Diaz-Gonzalez
b54a5b3f67 feat: add more lintian overrides for golang-based builds 2017-03-31 17:50:54 -06:00
Jose Diaz-Gonzalez
5b33bd5eda fix: clean up src files in tmp build dir 2017-03-31 17:50:34 -06:00
Jose Diaz-Gonzalez
6d55dd8884 fix: allow skipping go-clean 2017-03-31 17:50:16 -06:00
Jose Diaz-Gonzalez
0fae350256 fix: do not clean src files between builds
This breaks packaging for multiple systems, and should only be done in the tmp build dir.
2017-03-31 17:48:26 -06:00
Jose Diaz-Gonzalez
90015ab594 fix: ensure the Makefile is also removed when calling make src-clean 2017-03-31 17:27:08 -06:00
Jose Diaz-Gonzalez
28281ae4d4 fix: properly detect release builds 2017-03-31 17:08:03 -06:00
Jose Diaz-Gonzalez
4723b27f2c Merge pull request #2690 from znz/fix-contrib/release
Fix some bugs in contrib/release
2017-03-31 16:29:21 -06:00
Kazuhiro NISHIYAMA
de338f4da5 Fix a typo 2017-03-30 21:32:07 +09:00
Kazuhiro NISHIYAMA
d52eac7b35 Fix a typo 2017-03-30 21:31:15 +09:00
Kazuhiro NISHIYAMA
4d01e677ee Add missing $ 2017-03-30 21:30:33 +09:00
Kazuhiro NISHIYAMA
33daa88197 Add missing local 2017-03-30 21:29:59 +09:00
Kazuhiro NISHIYAMA
607a3d88ae CURRENT_VERSION used in fn-repo-update-history-and-commit 2017-03-30 21:27:35 +09:00
Kazuhiro NISHIYAMA
be082e2fe0 Escaping CURRENT_VERSION to avoid too replaced
like change fixed in 5173145653
2017-03-30 21:24:40 +09:00
Kazuhiro NISHIYAMA
27bd578405 $IS_RELEASE is true or false
`[[ "$IS_RELEASE" == "release" ]]` does never match,
because `fn-is-release` outputs `true` or `false` only.
2017-03-30 21:23:15 +09:00
Jose Diaz-Gonzalez
0962ebeadb Merge pull request #2689 from mbreit/dokku-monit-plugin
Add dokku-monit to community plugin list
2017-03-29 09:19:03 -06:00
Moritz Breit
95d9ca8945 Add dokku-monit to community plugin list 2017-03-29 10:00:43 +02:00
Jose Diaz-Gonzalez
e71cd7a939 Merge pull request #2684 from dokku/automated-release
feat: Allow triggering a release via the `contrib/release` script
2017-03-27 23:24:53 -06:00
Jose Diaz-Gonzalez
4bf301c913 chore: group single-line directives that are the same
[ci skip]
2017-03-27 09:32:48 -06:00
Jose Diaz-Gonzalez
0ea9e398bb Merge pull request #2685 from dokku/dockerfile-packaging
Pass shellcheck on os x
2017-03-26 16:23:33 -06:00
Jose Diaz-Gonzalez
2a6ea5d979 Merge pull request #2677 from callahad/prefer-http2
Prefer HTTP2 to SPDY in nginx-vhosts
2017-03-26 13:15:47 -06:00
Jose Diaz-Gonzalez
b8cde503c2 SC2178: Variable was used as an array but is now assigned a string 2017-03-26 05:03:16 -06:00
Jose Diaz-Gonzalez
89c8e1c6a5 SC2086: Double quote to prevent globbing and word splitting 2017-03-26 05:02:03 -06:00
Jose Diaz-Gonzalez
6c66e9850c SC2145: Argument mixes string and array. Use * or separate argument. 2017-03-26 05:00:43 -06:00
Jose Diaz-Gonzalez
fbcc8d8d6c SC2154: disable 2017-03-26 04:59:54 -06:00
Jose Diaz-Gonzalez
e2bbdc968d SC2162: read without -r mangle backslashes 2017-03-26 04:59:10 -06:00
Jose Diaz-Gonzalez
ed7d5623ad fix: attempt to pass shellcheck on os x 2017-03-26 04:38:35 -06:00
Jose Diaz-Gonzalez
217e491e59 feat: Allow triggering a release via the contrib/release script
- Requires docker, git, and package_cloud gems
- Requires the PACKAGECLOUD_API_TOKEN environment variable
- Builds both a deb and rpm, pushing both to packagecloud
- Upgrades the golang build environment to 1.7.5
- Combines both build Dockerfiles into a single, generic env
- Fixes issue where internal-functions file for tar plugin was not executable
- Ensures that built plugins have a callback to clean up their source files before packaging
- Adds support for “betafish” releases, which are untested versions of Dokku that are available via a separate packagecloud repository.
2017-03-25 17:56:42 -06:00
Jose Diaz-Gonzalez
101414dcd3 Merge pull request #2683 from dokku/josegonzalez-patch-1
docs: ensure we have an example for adding keys as another user
2017-03-24 17:35:20 -06:00
Jose Diaz-Gonzalez
edda1e6567 docs: ensure we have an example for adding keys as another user
Closes #2680

[ci skip]
2017-03-24 17:35:05 -06:00
Jose Diaz-Gonzalez
dd7439095a Merge pull request #2682 from dokku/josegonzalez-patch-1
docs: Clarify supported stanzas in app.json
2017-03-24 16:08:31 -06:00
Jose Diaz-Gonzalez
9e3ae9c37c docs: Clarify supported stanzas in app.json
Closes #2681

[ci skip]
2017-03-24 16:08:17 -06:00
Jose Diaz-Gonzalez
b1bd2c17a0 Merge pull request #2679 from callahad/remove-linode-docs
Remove unnecessary Linode-specific instructions
2017-03-23 14:58:48 -06:00
Dan Callahan
a939296981 Prefer HTTP2 to SPDY in nginx-vhosts
1.  SPDY has been deprecated and removed from modern browsers:
    http://caniuse.com/#feat=spdy

2.  HTTP/2 is present in all modern browsers:
    http://caniuse.com/#feat=http2

3.  SPDY and HTTP/2 are mutually exclusive in port-based `listen` directives:
    http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

Caniuse suggests that 74% of global users have a browser that supports
HTTP/2, while only 27% have a browser that supports SPDY. Only IE11 and
Safari support both.

Therefore, when both are available, Dokku should choose HTTP/2 to
maximize the benefit to users.
2017-03-23 15:23:57 -05:00
Dan Callahan
23cfccf424 Remove unnecessary Linode-specific instructions
Modern versions of Docker work just fine on Linode's default kernel
thanks to the `overlay2` storage driver.

Closes #2675
2017-03-23 15:18:10 -05:00
Jose Diaz-Gonzalez
eae2876187 Merge pull request #2673 from dokku/mh/chore/bump-herokuish
update to herokuish 0.3.27
2017-03-23 13:43:24 -06:00
Jose Diaz-Gonzalez
8730bbf0a9 Merge pull request #2674 from dokku/mh/chore/bump-sshcommand
update sshcommand to 0.7.0
2017-03-23 13:43:04 -06:00
Jose Diaz-Gonzalez
1f5e5e5c14 Merge pull request #2654 from ebeigarts/rpm-enable-docker-nginx
Enable nginx and docker on system startup when using bootstrap.sh on CentOS
2017-03-23 13:42:45 -06:00
Michael Hobbs
e8b6e33934 update sshcommand to 0.7.0 2017-03-22 09:33:53 -07:00
Jose Diaz-Gonzalez
9ee6c7aab6 Merge pull request #2671 from znz/fix-var-name
Fix variable name
2017-03-22 10:31:48 -06:00
Jose Diaz-Gonzalez
22a8b914d2 Merge pull request #2670 from znz/fix-typo
Remove duplicated `(i.e. `
2017-03-22 09:59:03 -06:00
Jose Diaz-Gonzalez
dfab96c72e Merge pull request #2672 from callahad/fix-logrotate-debian
Fix logrotate on Debian
2017-03-22 09:58:01 -06:00
Michael Hobbs
7c8fba8fc1 update to herokuish 0.3.27 2017-03-22 08:52:50 -07:00
Dan Callahan
85f4023965 Fix logrotate on Debian
Since we're creating logs as `root:dokku`, the logrotate configuration
needs an `su root dokku` line, just like `su syslog dokku` on ubuntu.
2017-03-22 08:39:46 -05:00
Kazuhiro NISHIYAMA
e9ee47a7c7 Fix variable name
`key` may not be `DOKKU_ROOT`.
2017-03-22 21:31:27 +09:00
Kazuhiro NISHIYAMA
a7c6127f2b Remove duplicated (i.e.
[ci skip]
2017-03-22 21:29:21 +09:00
Jose Diaz-Gonzalez
4e6a699140 Merge pull request #2666 from dokku/josegonzalez-patch-1
Use correct flag for build arguments when installing herokuish
2017-03-21 08:15:29 -06:00
Jose Diaz-Gonzalez
f71644faf6 Use correct flag for build arguments when installing herokuish
Closes #2665

[ci skip]
2017-03-21 08:15:07 -06:00
Michael Hobbs
9a6306c53b Merge pull request #2546 from dokku/mh-repo-golang
convert repo plugin to golang
2017-03-20 11:08:19 -07:00
Michael Hobbs
83d98fb6fb cleanup from master rebase 2017-03-20 09:18:54 -07:00
Michael Hobbs
c501574753 fix overlooked rename of struct method receiver var 2017-03-20 09:16:49 -07:00
Michael Hobbs
f0efdd2ed4 rename DokkuCmd to ShellCmd and use strings.Join instead of fmt.Sprintf 2017-03-20 09:16:49 -07:00
Michael Hobbs
177b45c49b use common.mk for common make variables. use -e when calling make. use golang:1.7.4 image. 2017-03-20 09:16:49 -07:00
Michael Hobbs
fc31b28ab6 restructure to build individual binaries 2017-03-20 09:16:49 -07:00
Michael Hobbs
e61475c7ea add plugin docs regarding compiled languages and command API 2017-03-20 09:15:43 -07:00
Michael Hobbs
c92c0fce37 remove non-existent targets from .PHONY 2017-03-20 09:15:43 -07:00
Michael Hobbs
1a0c308b65 better error msg language 2017-03-20 09:15:43 -07:00
Michael Hobbs
dffbad49b4 refactor out variadic form of GetDeployingAppImageName 2017-03-20 09:15:43 -07:00
Michael Hobbs
b6dffaabee convert repo plugin to golang 2017-03-20 09:15:43 -07:00
Jose Diaz-Gonzalez
8d83803763 Merge pull request #2664 from pvalentim/fix-remote-option
Fix remote name when using --remote option with apps:create
2017-03-20 09:43:37 -06:00
Pedro Valentim
85e5bd7c86 Fix remote name when using --remote option with apps:create 2017-03-20 13:11:14 +00:00
Edgars Beigarts
7ba063c6bb Enable nginx and docker on system startup when using bootstrap.sh on CentOS 2017-03-15 14:52:29 +02:00
135 changed files with 3521 additions and 381 deletions

View File

@@ -14,3 +14,8 @@ indent_size = 4
insert_final_newline = true
indent_style = tab
indent_size = 4
[*.go]
insert_final_newline = true
indent_style = space
indent_size = 4

View File

@@ -1,5 +1,72 @@
# History
## 0.9.4
### Documentation
- #2710: @josegonzalez Quiet output for git-related commands
## 0.9.3
### Bug Fixes
- #2706: @josegonzalez fix: ensure nginx conf.d directory exists when running nginx install hook
- #2701: @scjody Set SSH_USER for root commands
### New Features
- #2708: @josegonzalez Document that we will not do buildpack support in the issue tracker
### Documentation
- #2709: @michaelshobbs increase CURL_TIMEOUT and CURL_CONNECT_TIMEOUT defaults
- #2699: @mbreit Add support for git >= 2.11
## 0.9.2
### New Features
- #2698: @josegonzalez docs: Document that we only care about specific sections
- #2697: @callahad Restore installer note regarding AUFS on Linode
- #2694: @scjody Add documentation note re: git-pre-pull vs. auth
### Documentation
- #2695: @michaelshobbs add tests for pre/post deploy tasks
## 0.9.1
### Bug Fixes
- #2693: @josegonzalez fix: explicitly chown data and data/storage directories
## 0.9.0
### Bug Fixes
- #2691: @josegonzalez Fix package building when golang binaries are available
- #2671: @znz Fix variable name
- #2672: @callahad Fix logrotate on Debian
- #2666: @josegonzalez Use correct flag for build arguments when installing herokuish
- #2664: @pvalentim Fix remote name when using --remote option with apps:create
### New Features
- #2689: @mbreit Add dokku-monit to community plugin list
- #2683: @josegonzalez Ensure we have an example for adding keys as another user
- #2682: @josegonzalez Clarify supported stanzas in app.json
- #2679: @callahad Remove unnecessary Linode-specific instructions
- #2670: @znz Remove duplicated `(i.e. `
### Documentation
- #2685: @josegonzalez Pass shellcheck on os x
- #2677: @callahad Prefer HTTP2 to SPDY in nginx-vhosts
- #2673: @michaelshobbs Update to herokuish 0.3.27
- #2674: @michaelshobbs Update sshcommand to 0.7.0
- #2654: @ebeigarts Enable nginx and docker on system startup when using bootstrap.sh on CentOS
- #2546: @michaelshobbs Convert repo plugin to golang
## 0.8.2
### New Features
@@ -1091,7 +1158,7 @@ Thanks to the *many* contributors for making this release our best release so fa
- #1473: @josegonzalez Handle crashing containers by using restart=on-failure policy
- #1476: @michaelshobbs Support static nginx port when deploying without an application VHOST
- #1476: nginx proxy without VHOST
- #1477: @arthurschreiber Support removing config variables that contain `\n`.
- #1477: @arthurschreiber Support removing config variables containing newlines.
### Documentation
@@ -1237,7 +1304,7 @@ This release pegs Dokku to Docker 1.6.2. Docker 1.7.0 introduced changes in `doc
### New Features
- #1245: @arthurschreiber Support config variables containing `\n`
- #1245: @arthurschreiber Support config variables containing newlines
- #1257: @josegonzalez Split nginx ssl logs by $APP
### Bug Fixes

View File

@@ -1,3 +1,5 @@
> If you need help figuring out how to use a specific buildpack, or are having issues using multiple buildpacks, please see our [irc or slack channels](http://dokku.viewdocs.io/dokku/getting-started/where-to-get-help/#the-irc-and-slack-channels). Issues pertaining to buildpacks may be closed and locked.
Description of problem:

View File

@@ -8,6 +8,7 @@ PREBUILT_STACK_URL ?= gliderlabs/herokuish:latest
DOKKU_LIB_ROOT ?= /var/lib/dokku
PLUGINS_PATH ?= ${DOKKU_LIB_ROOT}/plugins
CORE_PLUGINS_PATH ?= ${DOKKU_LIB_ROOT}/core-plugins
PLUGIN_MAKE_TARGET ?= build-in-docker
# If the first argument is "vagrant-dokku"...
ifeq (vagrant-dokku,$(firstword $(MAKECMDGOALS)))
@@ -23,7 +24,9 @@ else
BUILD_STACK_TARGETS = build-in-docker
endif
.PHONY: all apt-update install version copyfiles man-db plugins dependencies sshcommand plugn docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku
include common.mk
.PHONY: all apt-update install version copyfiles man-db plugins dependencies sshcommand plugn docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku go-build
include tests.mk
include deb.mk
@@ -50,7 +53,24 @@ package_cloud:
packer:
packer build contrib/packer.json
go-build:
basedir=$(PWD); \
for dir in plugins/*; do \
if [ -e $$dir/Makefile ]; then \
$(MAKE) -e -C $$dir $(PLUGIN_MAKE_TARGET) || exit $$? ;\
fi ;\
done
go-clean:
basedir=$(PWD); \
for dir in plugins/*; do \
if [ -e $$dir/Makefile ]; then \
$(MAKE) -e -C $$dir clean ;\
fi ;\
done
copyfiles:
$(MAKE) go-build || exit 1
cp dokku /usr/local/bin/dokku
mkdir -p ${CORE_PLUGINS_PATH} ${PLUGINS_PATH}
rm -rf ${CORE_PLUGINS_PATH}/*
@@ -62,11 +82,15 @@ copyfiles:
rm -rf ${CORE_PLUGINS_PATH}/$$plugin && \
rm -rf ${PLUGINS_PATH}/$$plugin && \
cp -R plugins/$$plugin ${CORE_PLUGINS_PATH}/available && \
rm -rf ${CORE_PLUGINS_PATH}/available/$$plugin/src && \
ln -s ${CORE_PLUGINS_PATH}/available/$$plugin ${PLUGINS_PATH}/available; \
find /var/lib/dokku/ -xtype l -delete;\
PLUGIN_PATH=${CORE_PLUGINS_PATH} plugn enable $$plugin ;\
PLUGIN_PATH=${PLUGINS_PATH} plugn enable $$plugin ;\
done
done
ifndef SKIP_GO_CLEAN
$(MAKE) go-clean
endif
chown dokku:dokku -R ${PLUGINS_PATH} ${CORE_PLUGINS_PATH} || true
$(MAKE) addman

View File

@@ -99,8 +99,8 @@ A fresh VM running any of the following operating systems:
To install the latest stable release, run the following commands as a user who has access to `sudo`:
```shell
wget https://raw.githubusercontent.com/dokku/dokku/v0.8.2/bootstrap.sh
sudo DOKKU_TAG=v0.8.2 bash bootstrap.sh
wget https://raw.githubusercontent.com/dokku/dokku/v0.9.4/bootstrap.sh
sudo DOKKU_TAG=v0.9.4 bash bootstrap.sh
```
You can then proceed to the ip address or domain name associated with your server to complete the web-based installation.

View File

@@ -118,8 +118,8 @@ install-dokku-from-deb-package() {
echo "--> Installing docker"
if uname -r | grep -q linode; then
echo "--> NOTE: Using Linode? Docker might complain about missing AUFS support."
echo " See http://dokku.viewdocs.io/dokku/getting-started/install/linode/"
echo "--> NOTE: Using Linode? Docker may complain about missing AUFS support."
echo " You can safely ignore this warning."
echo " Installation will continue in 10 seconds."
sleep 10
fi
@@ -167,6 +167,10 @@ install-dokku-from-rpm-package() {
yum -y install herokuish dokku
fi
echo "--> Enabling docker and nginx on system startup"
systemctl enable docker
systemctl enable nginx
echo "--> Starting nginx"
systemctl start nginx
}

4
common.mk Normal file
View File

@@ -0,0 +1,4 @@
GO_REPO_ROOT := /go/src/github.com/dokku/dokku
BUILD_IMAGE := golang:1.7.5
.PHONY: build-in-docker build clean src-clean

View File

@@ -1,34 +0,0 @@
FROM ubuntu:14.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get -y install gcc git build-essential wget ruby-dev ruby1.9.1 lintian rpm help2man man-db
RUN command -v fpm > /dev/null || sudo gem install fpm --no-ri --no-rdoc
WORKDIR /dokku
COPY Makefile /dokku/
COPY *.mk /dokku/
RUN make deb-setup rpm-setup
COPY . /dokku
RUN make sshcommand plugn version copyfiles
ARG DOKKU_VERSION=master
ENV DOKKU_VERSION ${DOKKU_VERSION}
ARG DOKKU_GIT_REV
ENV DOKKU_GIT_REV ${DOKKU_GIT_REV}
ARG IS_RELEASE=false
ENV IS_RELEASE ${IS_RELEASE}
RUN make rpm-herokuish rpm-dokku rpm-plugn rpm-sshcommand rpm-sigil
RUN mkdir -p /data && cp /tmp/*.rpm /data && ls /data/

View File

@@ -1,34 +0,0 @@
FROM ubuntu:14.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get -y install gcc git build-essential wget ruby-dev ruby1.9.1 lintian rpm help2man man-db
RUN command -v fpm > /dev/null || sudo gem install fpm --no-ri --no-rdoc
WORKDIR /dokku
COPY Makefile /dokku/
COPY *.mk /dokku/
RUN make deb-setup rpm-setup
COPY . /dokku
RUN make sshcommand plugn version copyfiles
ARG DOKKU_VERSION=master
ENV DOKKU_VERSION ${DOKKU_VERSION}
ARG DOKKU_GIT_REV
ENV DOKKU_GIT_REV ${DOKKU_GIT_REV}
ARG IS_RELEASE=false
ENV IS_RELEASE ${IS_RELEASE}
RUN make deb-herokuish deb-dokku deb-plugn deb-sshcommand deb-sigil
RUN mkdir -p /data && cp /tmp/*.deb /data && ls /data/

44
contrib/build.Dockerfile Normal file
View File

@@ -0,0 +1,44 @@
FROM ubuntu:14.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get -y install gcc git build-essential wget ruby-dev ruby1.9.1 lintian rpm help2man man-db
RUN command -v fpm > /dev/null || sudo gem install fpm --no-ri --no-rdoc
ARG GOLANG_VERSION
RUN wget -qO /tmp/go${GOLANG_VERSION}.linux-amd64.tar.gz https://storage.googleapis.com/golang/go${GOLANG_VERSION}.linux-amd64.tar.gz \
&& tar -C /usr/local -xzf /tmp/go${GOLANG_VERSION}.linux-amd64.tar.gz
ARG WORKDIR=/go/src/github.com/dokku/dokku
WORKDIR ${WORKDIR}
COPY Makefile ${WORKDIR}/
COPY *.mk ${WORKDIR}/
RUN make deb-setup rpm-setup sshcommand plugn
COPY . ${WORKDIR}
ARG PLUGIN_MAKE_TARGET
ARG DOKKU_VERSION=master
ARG DOKKU_GIT_REV
ARG IS_RELEASE=false
RUN PATH=$PATH:/usr/local/go/bin GOPATH=/go \
PLUGIN_MAKE_TARGET=${PLUGIN_MAKE_TARGET} \
DOKKU_VERSION=${DOKKU_VERSION} \
DOKKU_GIT_REV=${DOKKU_GIT_REV} \
IS_RELEASE=${IS_RELEASE} \
SKIP_GO_CLEAN=true \
make version copyfiles \
&& rm -rf plugins/common/*.go plugins/common/glide* plugins/common/vendor/ \
&& make deb-herokuish deb-dokku deb-plugn deb-sshcommand deb-sigil \
rpm-herokuish rpm-dokku rpm-plugn rpm-sshcommand rpm-sigil
RUN mkdir -p /data \
&& cp /tmp/*.deb /data \
&& cp /tmp/*.rpm /data \
&& ls -lha /data/

View File

@@ -10,7 +10,7 @@ import subprocess
import sys
import threading
VERSION = 'v0.8.2'
VERSION = 'v0.9.4'
hostname = ''
try:

View File

@@ -56,7 +56,7 @@ fn-dokku-host() {
if [[ -z "$DOKKU_HOST" ]]; then
fn-client-help-msg
fi
echo "$DOKKU_HOST"
}
@@ -125,7 +125,7 @@ main() {
else
APP="$APP_ARG"
fi
if git remote add dokku "dokku@$DOKKU_REMOTE_HOST:$APP"; then
if git remote add "$DOKKU_GIT_REMOTE" "dokku@$DOKKU_REMOTE_HOST:$APP"; then
echo "-----> Dokku remote added at ${DOKKU_REMOTE_HOST} called ${DOKKU_GIT_REMOTE}"
echo "-----> Application name is ${APP}"
else

313
contrib/release Executable file
View File

@@ -0,0 +1,313 @@
#!/usr/bin/env bash
set -eo pipefail
[[ $TRACE ]] && set -x
readonly ROOT_DIR="$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd)"
readonly TMP_WORK_DIR="$(mktemp -d "/tmp/dokku-release.XXXX")"
readonly DOKKU_GIT_REV="$(git rev-parse HEAD)"
trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' RETURN INT TERM EXIT
log-info() {
# shellcheck disable=SC2034
declare desc="Log info formatter"
echo "$*"
}
log-error() {
# shellcheck disable=SC2034
declare desc="Log error formatter"
echo "! $*" 1>&2
}
log-fail() {
# shellcheck disable=SC2034
declare desc="Log fail formatter"
log-error "$*"
exit -1
}
fn-build-dokku() {
declare desc="Builds dokku packages within a docker container"
declare IS_RELEASE="$1" DOKKU_VERSION="$2"
local NAME GOLANG_VERSION
if [[ -z "$DOKKU_VERSION" ]]; then
log-error "Invalid deb file specified"
return 1
fi
NAME="dokku:build"
[[ "$IS_RELEASE" == "true" ]] && NAME="dokku:build-release"
[[ -n "$DOKKU_VERSION" ]] || DOKKU_VERSION="master"
pushd "$ROOT_DIR" >/dev/null
GOLANG_VERSION="$(grep BUILD_IMAGE common.mk | cut -d' ' -f3 | cut -d':' -f2)"
docker build \
--build-arg DOKKU_VERSION="$DOKKU_VERSION" \
--build-arg DOKKU_GIT_REV="$DOKKU_GIT_REV" \
--build-arg IS_RELEASE="$IS_RELEASE" \
--build-arg PLUGIN_MAKE_TARGET="build" \
--build-arg GOLANG_VERSION="$GOLANG_VERSION" \
-f "contrib/build.Dockerfile" \
-t "$NAME" .
}
fn-extract-package() {
declare desc="Extract packages from a docker container to the root directory"
declare IS_RELEASE="$1" PACKAGE_NAME="$2"
local NAME
if [[ -z "$PACKAGE_NAME" ]]; then
log-error "Invalid deb file specified"
return 1
fi
NAME="dokku:build"
[[ "$IS_RELEASE" == "true" ]] && NAME="dokku:build-release"
log-info "(extract-package) writing ${PACKAGE_NAME} to correct path"
docker run --rm --entrypoint cat "$NAME" "/data/${PACKAGE_NAME}" > "${ROOT_DIR}/${PACKAGE_NAME}"
}
fn-in-array() {
declare desc="return true if value ($1) is in list (all other arguments)"
local e
for e in "${@:2}"; do
[[ "$e" == "$1" ]] && return 0
done
return 1
}
fn-is-release() {
declare desc="Checks if a given run is a release run"
declare RELEASE="$1"
local IS_RELEASE=false
if [[ "$RELEASE" == "major" ]] || [[ "$RELEASE" == "minor" ]] || [[ "$RELEASE" == "patch" ]]; then
IS_RELEASE=true
fi
echo "$IS_RELEASE"
}
fn-version-current() {
declare desc="Retrieves the current version of dokku"
pushd "$ROOT_DIR" >/dev/null
grep Version debian/control | cut -d' ' -f2
}
fn-version-next() {
declare desc="Calculates the next version name of dokku"
declare CURRENT_VERSION="$1" RELEASE="$2"
local major minor patch short_object
major=$(echo "$CURRENT_VERSION" | awk '{split($0,a,"."); print a[1]}')
minor=$(echo "$CURRENT_VERSION" | awk '{split($0,a,"."); print a[2]}')
patch=$(echo "$CURRENT_VERSION" | awk '{split($0,a,"."); print a[3]}')
if [[ "$RELEASE" == "major" ]]; then
major=$((major + 1))
minor="0"
patch="0"
elif [[ "$RELEASE" == "minor" ]]; then
minor=$((minor + 1))
patch="0"
elif [[ "$RELEASE" == "patch" ]]; then
patch=$((patch + 1))
else
short_object=$(git log --oneline | tail -n 1 | cut -d' ' -f1)
echo "${CURRENT_VERSION}build+$(echo "$DOKKU_GIT_REV" | cut -c1-${#short_object})"
return 0
fi
echo "${major}.${minor}.${patch}"
}
fn-publish-package() {
declare desc="Publishes a package to packagecloud"
declare IS_RELEASE="$1" RELEASE_TYPE="$2" PACKAGE_NAME="$3"
local REPOSITORY=dokku/dokku-betafish DIST=ubuntu/trusty
[[ "$IS_RELEASE" == "true" ]] && REPOSITORY=dokku/dokku
[[ "$RELEASE_TYPE" == "rpm" ]] && DIST=el/7
log-info "(release-dokku) pushing ${RELEASE_TYPE} to packagecloud.com/${REPOSITORY}/${DIST}"
package_cloud push "${REPOSITORY}/${DIST}" "$PACKAGE_NAME"
}
fn-replace-version() {
declare desc="Replaces the version within a file"
local CURRENT_VERSION="$1"
local NEXT_VERSION="$2"
local FILENAME="$3"
sed -i.bak "s/${CURRENT_VERSION//./\.}/$NEXT_VERSION/g" "$FILENAME" && rm "$FILENAME.bak"
git add "$FILENAME"
}
fn-repo-merged-requests() {
declare desc="Retrieves all pull request ids since a given release of dokku"
declare TAG="$1"
git log "v${TAG}..HEAD" --oneline | grep "Merge pull request" | cut -d' ' -f 5 | cut -d '#' -f2
}
fn-repo-push-tags() {
declare desc="Pushes tags and the repository to github"
declare IS_RELEASE="$1"
if [[ "$IS_RELEASE" == "false" ]]; then
return
fi
pushd "$ROOT_DIR" >/dev/null
git push --tags origin master
}
fn-repo-update() {
declare desc="Updates files within the repository for a given release"
declare IS_RELEASE="$1" CURRENT_VERSION="$2" NEXT_VERSION="$3"
log-info "(release-dokku) Replacing ${CURRENT_VERSION} with ${NEXT_VERSION}"
for f in plugins/*/plugin.toml; do
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" "$f"
done
if [[ "$IS_RELEASE" == "false" ]]; then
return
fi
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" contrib/dokku-installer.py
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" debian/control
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/advanced-usage/plugin-management.md
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/assets/favicons/browserconfig.xml
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/assets/favicons/manifest.json
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/assets/style.css
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/development/release-process.md
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/home.html
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/getting-started/installation.md
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/template.html
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" README.md
if [[ "$RELEASE" == 'patch' ]]; then
fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/assets/versions.json
else
versions=$(python -c 'import json,sys;d=json.load(sys.stdin);d["max-versions"].append("'"${NEXT_VERSION}"'"); print json.dumps(d, indent=2, sort_keys=True)' <docs/assets/versions.json)
echo "$versions" >docs/assets/versions.json
git add docs/assets/versions.json
fi
if [[ "$IS_RELEASE" == "false" ]]; then
return
fi
log-info "(release-dokku) Adding HISTORY.md, committing and tagging"
fn-repo-update-history-and-commit "$CURRENT_VERSION" "$NEXT_VERSION"
git tag "v${NEXT_VERSION}"
}
fn-repo-update-history-and-commit() {
declare desc="Updates the history file and commits the changes"
declare CURRENT_VERSION="$1" NEXT_VERSION="$2"
local PULL_REQUEST_ID TITLE AUTHOR TYPE CHANGELOG_TEXT
local COMMIT_MESSAGE HISTORY HISTORY_CONTENTS HISTORY_DOCUMENTATION HISTORY_ENHANCEMENT HISTORY_BUG ISSUE_FILE
pushd "$ROOT_DIR" >/dev/null
mkdir -p /tmp/github-data
for PULL_REQUEST_ID in $(fn-repo-merged-requests "$CURRENT_VERSION"); do
ISSUE_FILE="/tmp/github-data/${PULL_REQUEST_ID}.json"
if [[ ! -f "$ISSUE_FILE" ]]; then
curl -sS "https://api.github.com/repos/dokku/dokku/issues/${PULL_REQUEST_ID}" >"$ISSUE_FILE"
fi
TITLE="$(jq -r '.title' "$ISSUE_FILE")"
AUTHOR="$(jq -r '.user.login' "$ISSUE_FILE")"
TYPE="$(jq -r '.labels[] | select( .name | contains("type") ) | .name' "$ISSUE_FILE" | cut -d' ' -f2)"
CHANGELOG_TEXT="- #${PULL_REQUEST_ID}: @${AUTHOR} ${TITLE}"
if [[ "$TYPE" == "bug" ]]; then
HISTORY_BUG="${HISTORY_BUG}"$'\n'"$CHANGELOG_TEXT"
elif [[ "$TYPE" == "documentation" ]]; then
HISTORY_DOCUMENTATION="${HISTORY_DOCUMENTATION}"$'\n'"$CHANGELOG_TEXT"
elif [[ "$TYPE" == "enhancement" ]]; then
HISTORY_ENHANCEMENT="${HISTORY_ENHANCEMENT}"$'\n'"$CHANGELOG_TEXT"
fi
done
HISTORY="# History"
HISTORY="${HISTORY}"$'\n\n'"## ${NEXT_VERSION}"
if [[ "$HISTORY_BUG" ]]; then
HISTORY="${HISTORY}"$'\n\n'"### Bug Fixes"
HISTORY="${HISTORY}"$'\n'"$HISTORY_BUG"
fi
if [[ "$HISTORY_DOCUMENTATION" ]]; then
HISTORY="${HISTORY}"$'\n\n'"### New Features"
HISTORY="${HISTORY}"$'\n'"$HISTORY_DOCUMENTATION"
fi
if [[ "$HISTORY_ENHANCEMENT" ]]; then
HISTORY="${HISTORY}"$'\n\n'"### Documentation"
HISTORY="${HISTORY}"$'\n'"$HISTORY_ENHANCEMENT"
fi
sed -i.bak '1d' HISTORY.md && rm HISTORY.md.bak
HISTORY_CONTENTS="$(cat HISTORY.md)"
echo -e "$HISTORY\n$HISTORY_CONTENTS" >HISTORY.md
git add HISTORY.md
COMMIT_MESSAGE="Release ${NEXT_VERSION}"$'\n\n'"${HISTORY}"
git commit -m "${COMMIT_MESSAGE}"
}
fn-require-bin() {
declare desc="Checks that a binary exists"
declare BINARY="$1"
if ! command -v "$BINARY" &>/dev/null; then
log-fail "Missing ${BINARY}, please install it"
fi
}
main() {
declare RELEASE="$1"
local CURRENT_VERSION NEXT_VERSION IS_RELEASE major minor patch versions
local VALID_RELEASE_LEVELS=("major" "minor" "patch" "betafish")
if [[ "$RELEASE" == '--trace' ]]; then
shift 1
RELEASE="$1"
TRACE=1 && set -x
fi
if [[ -z "$RELEASE" ]]; then
log-fail "Argument 1 must be one of [major, minor, patch, betafish], none given"
fi
if ! fn-in-array "$RELEASE" "${VALID_RELEASE_LEVELS[@]}"; then
log-fail "Argument 1 must be one of [major, minor, patch, betafish], '${RELEASE}' given"
fi
fn-require-bin "docker"
fn-require-bin "git"
fn-require-bin "package_cloud"
[[ -n "$PACKAGECLOUD_API_TOKEN" ]] || log-fail "Missing PACKAGECLOUD_API_TOKEN environment variable"
CURRENT_VERSION="$(fn-version-current)"
NEXT_VERSION="$(fn-version-next "$CURRENT_VERSION" "$RELEASE")"
IS_RELEASE="$(fn-is-release "$RELEASE")"
fn-repo-update "$IS_RELEASE" "$CURRENT_VERSION" "$NEXT_VERSION"
fn-build-dokku "$IS_RELEASE" "$NEXT_VERSION"
fn-extract-package "$IS_RELEASE" "dokku_${NEXT_VERSION}_amd64.deb"
fn-extract-package "$IS_RELEASE" "dokku-${NEXT_VERSION}-1.x86_64.rpm"
fn-publish-package "$IS_RELEASE" "deb" "dokku_${NEXT_VERSION}_amd64.deb"
fn-publish-package "$IS_RELEASE" "rpm" "dokku-${NEXT_VERSION}-1.x86_64.rpm"
fn-repo-push-tags "$IS_RELEASE"
}
main "$@"

22
deb.mk
View File

@@ -1,6 +1,6 @@
HEROKUISH_DESCRIPTION = 'Herokuish uses Docker and Buildpacks to build applications like Heroku'
HEROKUISH_REPO_NAME ?= gliderlabs/herokuish
HEROKUISH_VERSION ?= 0.3.26
HEROKUISH_VERSION ?= 0.3.27
HEROKUISH_ARCHITECTURE = amd64
HEROKUISH_PACKAGE_NAME = herokuish_$(HEROKUISH_VERSION)_$(HEROKUISH_ARCHITECTURE).deb
@@ -26,7 +26,7 @@ Simplifies running a single command over SSH, and
manages authorized keys (ACL) and users in order to do so.
endef
SSHCOMMAND_REPO_NAME ?= dokku/sshcommand
SSHCOMMAND_VERSION ?= 0.6.0
SSHCOMMAND_VERSION ?= 0.7.0
SSHCOMMAND_ARCHITECTURE = amd64
SSHCOMMAND_PACKAGE_NAME = sshcommand_$(SSHCOMMAND_VERSION)_$(SSHCOMMAND_ARCHITECTURE).deb
SSHCOMMAND_URL ?= https://raw.githubusercontent.com/dokku/sshcommand/v$(SSHCOMMAND_VERSION)/sshcommand
@@ -92,8 +92,8 @@ deb-herokuish:
@echo " sudo docker rmi gliderlabs/herokuish" >> /tmp/tmp/post-install
@echo "fi" >> /tmp/tmp/post-install
@echo "echo 'Importing herokuish into docker (around 5 minutes)'" >> /tmp/tmp/post-install
@echo 'if [[ ! -z $${http_proxy+x} ]]; then BUILDARGS="--build-args http_proxy=$$http_proxy"; fi' >> /tmp/tmp/post-install
@echo 'if [[ ! -z $${https_proxy+x} ]]; then BUILDARGS="$$BUILDARGS --build-args https_proxy=$$https_proxy"; fi' >> /tmp/tmp/post-install
@echo 'if [[ ! -z $${http_proxy+x} ]]; then BUILDARGS="--build-arg http_proxy=$$http_proxy"; fi' >> /tmp/tmp/post-install
@echo 'if [[ ! -z $${https_proxy+x} ]]; then BUILDARGS="$$BUILDARGS --build-arg https_proxy=$$https_proxy"; fi' >> /tmp/tmp/post-install
@echo 'if [[ ! -z $${BUILDARGS+x} ]]; then echo Adding proxy settings to docker build: $$BUILDARGS; fi' >> /tmp/tmp/post-install
@echo 'sudo docker build $$BUILDARGS -t gliderlabs/herokuish /var/lib/herokuish 1> /dev/null' >> /tmp/tmp/post-install
@@ -124,8 +124,12 @@ deb-dokku:
cp dokku /tmp/build/usr/bin
cp LICENSE /tmp/build/usr/share/doc/dokku/copyright
find . -name ".DS_Store" -depth -exec rm {} \;
$(MAKE) go-build
cp common.mk /tmp/build/var/lib/dokku/core-plugins/common.mk
cp -r plugins/* /tmp/build/var/lib/dokku/core-plugins/available
find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do cd /tmp/build/var/lib/dokku/core-plugins/available/$$plugin && if [ -e Makefile ]; then $(MAKE) src-clean; fi; done
find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do touch /tmp/build/var/lib/dokku/core-plugins/available/$$plugin/.core; done
rm /tmp/build/var/lib/dokku/core-plugins/common.mk
$(MAKE) help2man
$(MAKE) addman
cp /usr/local/share/man/man1/dokku.1 /tmp/build/usr/share/man/man1/dokku.1
@@ -140,16 +144,16 @@ endif
ifneq (,$(findstring false,$(IS_RELEASE)))
sed -i.bak -e "s/^/`date +%s`:/" /tmp/build/var/lib/dokku/STABLE_VERSION && rm /tmp/build/var/lib/dokku/STABLE_VERSION.bak
endif
rm /tmp/build/DEBIAN/lintian-overrides
mv debian/lintian-overrides /tmp/build/usr/share/lintian/overrides/dokku
rm -f /tmp/build/DEBIAN/lintian-overrides
cp debian/lintian-overrides /tmp/build/usr/share/lintian/overrides/dokku
ifdef DOKKU_GIT_REV
echo "$(DOKKU_GIT_REV)" > /tmp/build/var/lib/dokku/GIT_REV
else
git rev-parse HEAD > /tmp/build/var/lib/dokku/GIT_REV
endif
sed -i "s/^Version: .*/Version: `cat /tmp/build/var/lib/dokku/STABLE_VERSION`/g" /tmp/build/DEBIAN/control
dpkg-deb --build /tmp/build "/tmp/dokku_`cat /tmp/build/var/lib/dokku/STABLE_VERSION`_$(DOKKU_ARCHITECTURE).deb"
lintian "/tmp/dokku_`cat /tmp/build/var/lib/dokku/STABLE_VERSION`_$(DOKKU_ARCHITECTURE).deb"
sed -i.bak "s/^Version: .*/Version: `cat /tmp/build/var/lib/dokku/STABLE_VERSION`/g" /tmp/build/DEBIAN/control && rm /tmp/build/DEBIAN/control.bak
dpkg-deb --build /tmp/build "/tmp/dokku_`cat /tmp/build/var/lib/dokku/VERSION`_$(DOKKU_ARCHITECTURE).deb"
lintian "/tmp/dokku_`cat /tmp/build/var/lib/dokku/VERSION`_$(DOKKU_ARCHITECTURE).deb"
deb-plugn:
rm -rf /tmp/tmp /tmp/build $(PLUGN_PACKAGE_NAME)

2
debian/control vendored
View File

@@ -1,5 +1,5 @@
Package: dokku
Version: 0.8.2
Version: 0.9.4
Section: web
Priority: optional
Architecture: amd64

View File

@@ -1,3 +1,5 @@
dokku: command-with-path-in-maintainer-script postinst:16 /usr/bin/sshcommand
dokku: maintainer-script-calls-systemctl postinst:87
dokku: changelog-file-missing-in-native-package
dokku: statically-linked-binary
dokku: unstripped-binary-or-object

2
debian/postinst vendored
View File

@@ -35,7 +35,7 @@ case "$1" in
echo "Setting up storage directories"
mkdir -p ${DOKKU_LIB_ROOT}/data ${DOKKU_LIB_ROOT}/data/storage
chown dokku:dokku -R ${DOKKU_LIB_ROOT}/data
chown dokku:dokku ${DOKKU_LIB_ROOT}/data ${DOKKU_LIB_ROOT}/data/storage
echo "Setting up plugin directories"
# should be replaced by `plugn init`

View File

@@ -20,40 +20,15 @@ For dockerfile apps, place `app.json` in the configured `WORKDIR` directory; oth
### Example app.json
> NOTE: Only the `scripts.dokku.predeploy` and `scripts.dokku.postdeploy` tasks are supported by Dokku at this time. All other fields will be ignored and can be omitted.
```json
{
"name": "barebones nodejs",
"description": "A barebones Node.js app using Express 4.",
"keywords": [
"nodejs",
"express"
],
"repository": "https://github.com/michaelshobbs/node-js-sample",
"scripts": {
"dokku": {
"predeploy": "touch /app/predeploy.test",
"postdeploy": "curl https://some.external.api.service.com/deployment?state=success"
}
},
"env": {
"SECRET_TOKEN": {
"description": "A secret key for verifying the integrity of signed cookies.",
"value": "secret"
},
"WEB_CONCURRENCY": {
"description": "The number of processes to run.",
"generator": "echo 5"
}
},
"image": "gliderlabs/herokuish",
"addons": [
"dokku-postgres",
"dokku-redis"
],
"buildpacks": [
{
"url": "https://github.com/heroku/heroku-buildpack-nodejs"
}
]
}
}
```

View File

@@ -31,30 +31,30 @@ dokku plugin
```
plugn: dev
00_dokku-standard 0.8.2 enabled dokku core standard plugin
20_events 0.8.2 enabled dokku core events logging plugin
apps 0.8.2 enabled dokku core apps plugin
build-env 0.8.2 enabled dokku core build-env plugin
certs 0.8.2 enabled dokku core certificate management plugin
checks 0.8.2 enabled dokku core checks plugin
common 0.8.2 enabled dokku core common plugin
config 0.8.2 enabled dokku core config plugin
docker-options 0.8.2 enabled dokku core docker-options plugin
domains 0.8.2 enabled dokku core domains plugin
enter 0.8.2 enabled dokku core enter plugin
git 0.8.2 enabled dokku core git plugin
logs 0.8.2 enabled dokku core logs plugin
named-containers 0.8.2 enabled dokku core named containers plugin
nginx-vhosts 0.8.2 enabled dokku core nginx-vhosts plugin
plugin 0.8.2 enabled dokku core plugin plugin
proxy 0.8.2 enabled dokku core proxy plugin
ps 0.8.2 enabled dokku core ps plugin
repo 0.8.2 enabled dokku core repo plugin
shell 0.8.2 enabled dokku core shell plugin
ssh-keys 0.8.2 enabled dokku core ssh-keys plugin
storage 0.8.2 enabled dokku core storage plugin
tags 0.8.2 enabled dokku core tags plugin
tar 0.8.2 enabled dokku core tar plugin
00_dokku-standard 0.9.4 enabled dokku core standard plugin
20_events 0.9.4 enabled dokku core events logging plugin
apps 0.9.4 enabled dokku core apps plugin
build-env 0.9.4 enabled dokku core build-env plugin
certs 0.9.4 enabled dokku core certificate management plugin
checks 0.9.4 enabled dokku core checks plugin
common 0.9.4 enabled dokku core common plugin
config 0.9.4 enabled dokku core config plugin
docker-options 0.9.4 enabled dokku core docker-options plugin
domains 0.9.4 enabled dokku core domains plugin
enter 0.9.4 enabled dokku core enter plugin
git 0.9.4 enabled dokku core git plugin
logs 0.9.4 enabled dokku core logs plugin
named-containers 0.9.4 enabled dokku core named containers plugin
nginx-vhosts 0.9.4 enabled dokku core nginx-vhosts plugin
plugin 0.9.4 enabled dokku core plugin plugin
proxy 0.9.4 enabled dokku core proxy plugin
ps 0.9.4 enabled dokku core ps plugin
repo 0.9.4 enabled dokku core repo plugin
shell 0.9.4 enabled dokku core shell plugin
ssh-keys 0.9.4 enabled dokku core ssh-keys plugin
storage 0.9.4 enabled dokku core storage plugin
tags 0.9.4 enabled dokku core tags plugin
tar 0.9.4 enabled dokku core tar plugin
```
Installing a plugin is easy as well using the `plugin:install` command. This command will also trigger the `install` pluginhook on all existing plugins.

View File

@@ -2,10 +2,10 @@
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/mstile-70x70.png"/>
<square150x150logo src="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/mstile-150x150.png"/>
<square310x310logo src="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/mstile-310x310.png"/>
<wide310x150logo src="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/mstile-310x150.png"/>
<square70x70logo src="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/mstile-70x70.png"/>
<square150x150logo src="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/mstile-150x150.png"/>
<square310x310logo src="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/mstile-310x310.png"/>
<wide310x150logo src="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/mstile-310x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>

View File

@@ -2,37 +2,37 @@
"name": "Dokku",
"icons": [
{
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.8.2\/docs\/assets\/favicons\/android-chrome-36x36.png",
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.9.4\/docs\/assets\/favicons\/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.8.2\/docs\/assets\/favicons\/android-chrome-48x48.png",
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.9.4\/docs\/assets\/favicons\/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.8.2\/docs\/assets\/favicons\/android-chrome-72x72.png",
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.9.4\/docs\/assets\/favicons\/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.8.2\/docs\/assets\/favicons\/android-chrome-96x96.png",
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.9.4\/docs\/assets\/favicons\/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.8.2\/docs\/assets\/favicons\/android-chrome-144x144.png",
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.9.4\/docs\/assets\/favicons\/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.8.2\/docs\/assets\/favicons\/android-chrome-192x192.png",
"src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.9.4\/docs\/assets\/favicons\/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"

View File

@@ -35,13 +35,13 @@ h1 {
background-repeat: no-repeat;
}
.header .navbar-brand a {
background-image: url(https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/dokku.png);
background-image: url(https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/dokku.png);
text-indent: 40px;
}
.blurb {
color: #424242;
background-color: #ededed;
background-image: url(https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/gplaypattern.png);
background-image: url(https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/gplaypattern.png);
padding: 45px 0;
text-align: center;
}

View File

@@ -5,6 +5,7 @@
"0.5.8",
"0.6.5",
"0.7.2",
"0.8.2"
"0.8.2",
"0.9.4"
]
}

View File

@@ -91,6 +91,7 @@ Note: The following plugins have been supplied by our community and may not have
[luxifer]: https://github.com/luxifer
[Maciej Łebkowski]: https://github.com/mlebkowski
[matto1990]: https://github.com/matto1990
[mbreit]: https://github.com/mbreit
[mbriskar]: https://github.com/mbriskar
[michaelshobbs]: https://github.com/michaelshobbs
[mikecsh]: https://github.com/mikecsh
@@ -180,6 +181,7 @@ Note: The following plugins have been supplied by our community and may not have
| [Haproxy tcp load balancer](https://github.com/256dpi/dokku-haproxy) | [256dpi][] | 0.4.0+ |
| [Hostname](https://github.com/michaelshobbs/dokku-hostname) | [michaelshobbs][] | 0.4.0+ |
| [HTTP Auth Secure Apps](https://github.com/matto1990/dokku-secure-apps) | [matto1990][] | 0.4.0+ |
| [Monit (Health Checks)](https://github.com/mbreit/dokku-monit) | [mbreit][] | 0.8.0+ |
| [Nuke Containers](https://github.com/josegonzalez/dokku-nuke) | [josegonzalez][] | 0.4.0+ |
| [Open App Ports](https://github.com/josegonzalez/dokku-ports) | [josegonzalez][] | 0.3.x |
| [Proctype Filter](https://github.com/michaelshobbs/dokku-proctype-filter) | [michaelshobbs][] | 0.4.0+ |

View File

@@ -56,6 +56,13 @@ Admin users and root can also add keys remotely:
cat ~/.ssh/id_rsa.pub | ssh dokku@dokku.me ssh-keys:add KEY_NAME
```
If you are using an ssh user other than `dokku`, then you'll also need to specify the `dokku` bin:
```shell
cat ~/.ssh/id_rsa.pub | ssh root@dokku.me dokku ssh-keys:add KEY_NAME
```
Finally, if you are using the vagrant installation, you can also use the `make vagrant-acl-add` target to add your public key to Dokku (it will use your host username as the `USER`):
```shell

View File

@@ -1,5 +1,7 @@
# Plugin Creation
Plugins can simply be implementations of [triggers](/dokku/development/plugin-triggers) or they may implement a command structure of their own. Dokku has no restrictions on the language in which a plugin is implemented, it only cares that the plugin implements the appropriate [commands](/dokku/development/plugin-creation#command-api) or [triggers](/dokku/development/plugin-triggers) API. NOTE: all files that implement the triggers or commands API must be executable.
If you create your own plugin:
1. Take a look at the plugins shipped with Dokku and hack away!
@@ -9,6 +11,23 @@ If you create your own plugin:
5. Subscribe to the [dokku development blog](http://progrium.com) to be notified about API changes and releases
### Compilable Plugins (i.e. golang, java(?), c, etc.)
The plugin developer is required to implement the `install` trigger such that it will output your built executable(s) in the correct directory structure to implement the plugin's desired command and/or trigger API. See [smoke-test-plugin](https://github.com/dokku/smoke-test-plugin) for an example.
### Command API
There are 3 main implementation points: `commands`, `subcommands/default`, and `subcommands/<command-name>`
#### commands
Primarily used to supply the plugin's usage/help output. (i.e. [plugin help](https://github.com/dokku/dokku/tree/master/plugins/plugin/commands))
#### subcommands/default
Implements the plugin's default command behavior. (i.e. [`dokku plugin`](https://github.com/dokku/dokku/tree/master/plugins/plugin/subcommands/default))
#### subcommands/<command-name>
Implements the additional command interface and will translate to `dokku plugin:cmd` on the command line. (i.e. [`dokku plugin:install`](https://github.com/dokku/dokku/tree/master/plugins/plugin/subcommands/install))
### Sample plugin
The below plugin is a dummy `dokku hello` plugin.

View File

@@ -321,6 +321,10 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
# TODO
```
> WARNING: The `git-pre-pull` trigger should _not_ be used for authentication
since it does not get called for commands that use `git-upload-archive` such
as `git archive`. Instead, use the [`user-auth`](#user-auth) trigger.
### `install`
- Description: Used to setup any files/configuration for a plugin.

View File

@@ -67,7 +67,7 @@ The workflow looks like this:
```shell
# having dokku-arch in ../dokku-arch
vagrant up build-arch
# wait for "==> build-arch: ==> Finished making: dokku 0.8.2-2 (Mon Feb 22 23:20:37 CET 2016)"
# wait for "==> build-arch: ==> Finished making: dokku 0.9.4-2 (Mon Feb 22 23:20:37 CET 2016)"
cd ../dokku-arch
git add PKGBUILD .SRCINFO
git commit -m 'Update to dokku 0.9.9'

View File

@@ -1,28 +0,0 @@
# Installing on Linode
## Enable AUFS Storage Driver
When installing Dokku on a newly-created Linode server, you'll likely see an error during Docker installation:
> Warning: current kernel is not supported by the linux-image-extra-virtual package. We have no AUFS support. Consider installing the packages linux-image-virtual kernel and linux-image-extra-virtual for AUFS support.
After 10 seconds, the installation will continue as normal.
This warning is the result of Linode using its own kernel, which does not support AUFS, instead of using the kernel supplied by Debian/Ubuntu. If you ignore the warning, Docker will fall back to using the DeviceMapper storage backend and Dokku will work normally. However, AUFS is better tested and will perform better.
To avoid this error message and use AUFS, follow the steps below to select the GRUB 2 kernel.
### Select the GRUB 2 kernel instead of the default Linode kernel:
1. Login to your [Linode Manager](http://manager.linode.com), open the `Dashboard` associated with your new server.
2. Click `Edit` for the `Configuration Profile`. You should now be on the `Edit Configuration Profile` page.
3. Scroll down to `Boot Settings` > `Kernel menu`. Change the "Kernel" option to "GRUB 2" and save your changes.
4. If you have already pushed the `Boot` button and your server is running, you will need to reboot it before continuing. Otherwise, you can now push the `Boot` button to start your server and proceed with the normal Dokku installation.
Once your server comes back online, you'll be running Ubuntu's default kernel. You can now follow Dokku's [normal installation instructions](/dokku/getting-started/installation/) and `bootstrap.sh` will take care of everything else.
### Verify that you are using AUFS:
Once you have fully installed Dokku and rebooted your server, you can verify that AUFS is being used with the terminal command: `docker info`. If AUFS is being used, you should see `Storage Driver: aufs` in the output.

View File

@@ -7,16 +7,21 @@
Dokku defaults to being installed via RPM package on CentOS 7. While certain hosts may require extra work to get running, you may optionally wish to automate the installation of Dokku without the use of our `bootstrap.sh` bash script. The following are the steps run by said script:
```shell
# install docker
# Install docker
curl -fsSL https://get.docker.com/ | sh
# install epel for nginx packages to be available
# Install epel for nginx packages to be available
sudo yum install -y epel-release
# install dokku
# Install dokku
curl -s https://packagecloud.io/install/repositories/dokku/dokku/script.rpm.sh | sudo bash
sudo yum install -y herokuish dokku
sudo dokku plugin:install-dependencies --core
systemctl start nginx
# Enable docker and nginx on system startup
systemctl enable docker
systemctl enable nginx
```
The [devicemapper](https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/) is the default Docker storage driver on CentOS 7 and defaults to a configuration mode known as `loop-lvm`. This mode is designed to work out-of-the-box with no additional configuration. However, production deployments should not run under `loop-lvm` mode. The preferred configuration for production deployments is [`direct-lvm`](https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/#/configure-direct-lvm-mode-for-production).

View File

@@ -21,8 +21,8 @@ To install the latest stable version of dokku, you can run the following shell c
```shell
# for debian systems, installs Dokku via apt-get
wget https://raw.githubusercontent.com/dokku/dokku/v0.8.2/bootstrap.sh;
sudo DOKKU_TAG=v0.8.2 bash bootstrap.sh
wget https://raw.githubusercontent.com/dokku/dokku/v0.9.4/bootstrap.sh;
sudo DOKKU_TAG=v0.9.4 bash bootstrap.sh
```
The installation process takes about 5-10 minutes, depending upon internet connection speed.
@@ -44,7 +44,6 @@ For various reasons, certain hosting providers may have other steps that should
- [Digital Ocean Installation Notes](/dokku/getting-started/install/digitalocean)
- [DreamHost Cloud Installation Notes](/dokku/getting-started/install/dreamhost/)
- [Linode Installation Notes](/dokku/getting-started/install/linode/)
- [Microsoft Azure Installation Notes](/dokku/getting-started/install/azure/)
As well, you may wish to customize your installation in some other fashion. or experiment with vagrant. The guides below should get you started:

View File

@@ -10,30 +10,30 @@
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css">
<title>Dokku - The smallest PaaS implementation you've ever seen</title>
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon.ico">
<meta name="apple-mobile-web-app-title" content="Dokku">
<meta name="application-name" content="Dokku">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/browserconfig.xml">
<meta name="msapplication-TileImage" content="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.2/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/style.css" rel="stylesheet">
<link href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/style.css" rel="stylesheet">
<!-- <link href="/dokku/docs/assets/style.css" rel="stylesheet"> -->
<style>
</style>
@@ -98,12 +98,12 @@
<p class="line">
<span class="path"></span>
<span class="prompt">$</span>
<span class="command">wget https://raw.githubusercontent.com/dokku/dokku/v0.8.2/bootstrap.sh</span>
<span class="command">wget https://raw.githubusercontent.com/dokku/dokku/v0.9.4/bootstrap.sh</span>
</p>
<p class="line">
<span class="path"></span>
<span class="prompt">$</span>
<span class="command">sudo DOKKU_TAG=v0.8.2 bash bootstrap.sh</span>
<span class="command">sudo DOKKU_TAG=v0.9.4 bash bootstrap.sh</span>
</p>
<p class="line">
<span class="output">&nbsp;# go to your server's IP and follow the web installer</span>
@@ -173,7 +173,7 @@
</div>
<div class="alternate-instructions">
<p>Hate <code>curl | bash</code>? See our official <a href="https://github.com/Azure/azure-quickstart-templates/tree/master/dokku-vm" target="_blank">Azure</a>, <a href="https://www.digitalocean.com/features/one-click-apps/dokku/" target="_blank">DigitalOcean</a>, <a href="/{{NAME}}~{{REF}}/getting-started/install/dreamhost/">DreamHost Cloud</a>, and <a href="/{{NAME}}~{{REF}}/getting-started/install/linode/" target="_blank">Linode</a> instructions.<p>
<p>Hate <code>curl | bash</code>? See our official <a href="https://github.com/Azure/azure-quickstart-templates/tree/master/dokku-vm" target="_blank">Azure</a>, <a href="https://www.digitalocean.com/features/one-click-apps/dokku/" target="_blank">DigitalOcean</a>, and <a href="/{{NAME}}~{{REF}}/getting-started/install/dreamhost/">DreamHost Cloud</a> instructions.<p>
<p>Still no love? <a href="https://github.com/dokku/dokku/blob/master/CONTRIBUTING.md" target="_blank">Contributions welcome</a>!</a>
</div>
</div>

View File

@@ -9,26 +9,26 @@
<meta name="author" content="">
<title>Dokku - The smallest PaaS implementation you've ever seen</title>
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/android-chrome-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/manifest.json">
<link rel="shortcut icon" href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/favicon.ico">
<meta name="apple-mobile-web-app-title" content="Dokku">
<meta name="application-name" content="Dokku">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/favicons/browserconfig.xml">
<meta name="msapplication-TileImage" content="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/mstile-144x144.png">
<meta name="msapplication-config" content="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/favicons/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<script>
@@ -42,7 +42,7 @@
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.2/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.rawgit.com/dokku/dokku/v0.8.2/docs/assets/style.css" rel="stylesheet">
<link href="https://cdn.rawgit.com/dokku/dokku/v0.9.4/docs/assets/style.css" rel="stylesheet">
<!-- <link href="/dokku/docs/assets/style.css" rel="stylesheet"> -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet">
<style>

8
dokku
View File

@@ -56,8 +56,12 @@ if [[ $(id -un) != "dokku" ]] && [[ ! $1 =~ plugin:* ]] && [[ ! $1 == "ssh-keys:
exit $?
fi
if [[ $(id -un) != "root" && $1 =~ ^plugin:.* && $1 != "plugin:help" && $1 != "plugin:list" ]] || [[ $(id -un) != "root" && $1 == "ssh-keys:add" ]]; then
dokku_log_fail "This command must be run as root"
if [[ $1 =~ ^plugin:.* && $1 != "plugin:help" && $1 != "plugin:list" ]] || [[ $1 == "ssh-keys:add" ]]; then
if [[ $(id -un) != "root" ]]; then
dokku_log_fail "This command must be run as root"
else
export SSH_USER=root
fi
fi
if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core standard plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -10,6 +10,7 @@ flag_rsyslog_needs_restart=n
# This can be done unconditionally as mkdir -p
# exits gracefully if the path already exists
# shellcheck disable=SC2174
mkdir -m 775 -p "$DOKKU_LOGS_DIR"
case "$DOKKU_DISTRO" in
arch|debian|centos)
@@ -59,7 +60,7 @@ $DOKKU_LOGS_DIR/*.log {
EOF
if [[ "$DOKKU_DISTRO" = "debian" ]]; then
sed -i '/\s*su syslog dokku/d; s/\(create [0-7][0-7][0-7]\) syslog dokku/\1 root dokku/g' $DOKKU_LOGROTATE_FILE
sed -i 's/ syslog dokku$/root dokku/g' $DOKKU_LOGROTATE_FILE
fi
flag_rsyslog_needs_restart=y

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core events logging plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core apps plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core build-env plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -9,8 +9,8 @@ build_env_pre_build_buildpack() {
local APP="$1"; local IMAGE=$(get_app_image_name "$APP"); local BUILD_ENV=""
verify_app_name "$APP"
[[ -z $(config_get --global CURL_CONNECT_TIMEOUT) ]] && config_set --global CURL_CONNECT_TIMEOUT=5
[[ -z $(config_get --global CURL_TIMEOUT) ]] && config_set --global CURL_TIMEOUT=30
[[ -z $(config_get --global CURL_CONNECT_TIMEOUT) ]] && config_set --global CURL_CONNECT_TIMEOUT=90
[[ -z $(config_get --global CURL_TIMEOUT) ]] && config_set --global CURL_TIMEOUT=60
if [[ -n $(config_export global) ]]; then
local BUILD_ENV+=$'\n'

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core certificate management plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -74,7 +74,7 @@ checks_check_deploy() {
# after successfully copying the file. Thus, we suppress stderr.
# ref: https://github.com/dotcloud/docker/issues/3986
local CHECK_DEPLOY_TMP_WORK_DIR=$(mktemp -d /tmp/dokku_CHECKS.XXXXX)
docker cp $DOKKU_APP_CONTAINER_ID:/app/CHECKS "$CHECK_DEPLOY_TMP_WORK_DIR" 2> /dev/null || true
docker cp "$DOKKU_APP_CONTAINER_ID:/app/CHECKS" "$CHECK_DEPLOY_TMP_WORK_DIR" 2> /dev/null || true
local FILENAME=${CHECK_DEPLOY_TMP_WORK_DIR}/CHECKS
@@ -107,8 +107,8 @@ checks_check_deploy() {
dokku_log_info1 "Waiting for $DOKKU_DEFAULT_CHECKS_WAIT seconds ..."
sleep "$DOKKU_DEFAULT_CHECKS_WAIT"
! (is_container_running $DOKKU_APP_CONTAINER_ID) && dokku_log_fail "App container failed to start!!"
local container_restarts=$(docker inspect -f "{{ .RestartCount }}" $DOKKU_APP_CONTAINER_ID)
! (is_container_running "$DOKKU_APP_CONTAINER_ID") && dokku_log_fail "App container failed to start!!"
local container_restarts="$(docker inspect -f "{{ .RestartCount }}" "$DOKKU_APP_CONTAINER_ID")"
if [[ $container_restarts -ne 0 ]]; then
docker stop "$DOKKU_APP_CONTAINER_ID" || true
dokku_log_fail "App container failed to start!!"

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core checks plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

141
plugins/common/common.go Normal file
View File

@@ -0,0 +1,141 @@
package common
import (
"fmt"
"os"
"os/exec"
"regexp"
"strings"
sh "github.com/codeskyblue/go-sh"
)
// ShellCmd represents a shell command to be run for dokku
type ShellCmd struct {
Env map[string]string
Command *exec.Cmd
CommandString string
Args []string
ShowOutput bool
}
// NewShellCmd returns a new ShellCmd struct
func NewShellCmd(command string) *ShellCmd {
items := strings.Split(command, " ")
cmd := items[0]
args := items[1:]
return &ShellCmd{
Command: exec.Command(cmd, args...),
CommandString: command,
Args: args,
ShowOutput: true,
}
}
// Execute is a lightweight wrapper around exec.Command
func (sc *ShellCmd) Execute() bool {
env := os.Environ()
for k, v := range sc.Env {
env = append(env, fmt.Sprintf("%s=%s", k, v))
}
sc.Command.Env = env
if sc.ShowOutput {
sc.Command.Stdout = os.Stdout
sc.Command.Stderr = os.Stderr
}
err := sc.Command.Run()
if err != nil {
return false
}
return true
}
// VerifyAppName verifies app name format and app existence"
func VerifyAppName(appName string) (err error) {
dokkuRoot := MustGetEnv("DOKKU_ROOT")
appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
_, err = os.Stat(appRoot)
if os.IsNotExist(err) {
return fmt.Errorf("App %s does not exist: %v\n", appName, err)
}
r, _ := regexp.Compile("^[a-z].*")
if !r.MatchString(appName) {
return fmt.Errorf("App name (%s) must begin with lowercase alphanumeric character\n", appName)
}
return err
}
// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) string {
value := os.Getenv(key)
if value == "" {
LogFail(fmt.Sprintf("%s not set!", key))
}
return value
}
// LogFail is the failure log formatter
// prints text to stderr and exits with status 1
func LogFail(text string) {
fmt.Fprintln(os.Stderr, fmt.Sprintf("FAILED: %s", text))
os.Exit(1)
}
// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (imageName string) {
if appName == "" {
LogFail("(GetDeployingAppImageName) APP must not be empty")
}
b, err := sh.Command("plugn", "trigger", "deployed-app-repository", appName).Output()
if err != nil {
LogFail(err.Error())
}
imageRemoteRepository := string(b[:])
b, err = sh.Command("plugn", "trigger", "deployed-app-image-tag", appName).Output()
if err != nil {
LogFail(err.Error())
}
newImageTag := string(b[:])
b, err = sh.Command("plugn", "trigger", "deployed-app-image-repo", appName).Output()
if err != nil {
LogFail(err.Error())
}
newImageRepo := string(b[:])
if newImageRepo != "" {
imageRepo = newImageRepo
}
if newImageTag != "" {
imageTag = newImageTag
}
if imageRepo == "" {
imageRepo = GetAppImageRepo(appName)
}
if imageTag == "" {
imageTag = "latest"
}
imageName = fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
if !VerifyImage(imageName) {
LogFail(fmt.Sprintf("app image (%s) not found", imageName))
}
return
}
// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
return strings.Join([]string{"dokku", appName}, "/")
}
// VerifyImage returns true if docker image exists in local repo
func VerifyImage(image string) bool {
imageCmd := NewShellCmd(strings.Join([]string{"docker inspect", image}, " "))
imageCmd.ShowOutput = false
if imageCmd.Execute() {
return true
}
return false
}

View File

@@ -0,0 +1,38 @@
package common
import (
"os"
"testing"
. "github.com/onsi/gomega"
)
func TestGetEnv(t *testing.T) {
RegisterTestingT(t)
Expect(MustGetEnv("DOKKU_ROOT")).To(Equal("/home/dokku"))
}
func TestGetAppImageRepo(t *testing.T) {
RegisterTestingT(t)
Expect(GetAppImageRepo("testapp")).To(Equal("dokku/testapp"))
}
func TestVerifyImageInvalid(t *testing.T) {
RegisterTestingT(t)
Expect(VerifyImage("testapp")).To(Equal(false))
}
func TestVerifyAppNameInvalid(t *testing.T) {
RegisterTestingT(t)
err := VerifyAppName("1994testApp")
Expect(err).To(HaveOccurred())
}
func TestVerifyAppName(t *testing.T) {
RegisterTestingT(t)
dir := "/home/dokku/testApp"
os.MkdirAll(dir, 0644)
err := VerifyAppName("testApp")
Expect(err).NotTo(HaveOccurred())
os.RemoveAll(dir)
}

View File

@@ -142,7 +142,7 @@ verify_app_name() {
local APP="$1"
[[ ! -n "$APP" ]] && dokku_log_fail "(verify_app_name) APP must not be null"
if [[ ! "$APP" =~ ^[a-z].* && ! "$APP" =~ ^[0-9].* ]]; then
[[ -d "$DOKKU_ROOT/$APP" ]] && rm -rf "$DOKKU_ROOT/$APP"
[[ -d "$DOKKU_ROOT/$APP" ]] && rm -rf "${DOKKU_ROOT:?}/$APP"
dokku_log_fail "App name must begin with lowercase alphanumeric character"
fi
[[ ! -d "$DOKKU_ROOT/$APP" ]] && dokku_log_fail "App $APP does not exist"
@@ -211,7 +211,7 @@ get_running_image_tag() {
verify_app_name "$APP"
local CIDS=( $(get_app_container_ids "$APP") )
local RUNNING_IMAGE_TAG=$(docker inspect -f '{{ .Config.Image }}' ${CIDS[0]} 2>/dev/null | awk -F: '{ print $2 }' || echo '')
local RUNNING_IMAGE_TAG=$(docker inspect -f '{{ .Config.Image }}' "${CIDS[0]}" 2>/dev/null | awk -F: '{ print $2 }' || echo '')
echo "$RUNNING_IMAGE_TAG"
}
@@ -331,10 +331,11 @@ get_app_container_ids() {
get_app_running_container_ids() {
declare desc="return list of running docker container ids for given app and optional container_type"
local APP="$1" CONTAINER_TYPE="$2"
local CIDS
verify_app_name "$APP"
! (is_deployed "$APP") && dokku_log_fail "App $APP has not been deployed"
local CIDS=$(get_app_container_ids "$APP" "$CONTAINER_TYPE")
CIDS=$(get_app_container_ids "$APP" "$CONTAINER_TYPE")
for CID in $CIDS; do
local APP_CONTAINER_STATUS=$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null || true)
@@ -367,7 +368,7 @@ get_cmd_from_procfile() {
if [[ -f $DOKKU_PROCFILE ]]; then
local line; local name; local command
while read line || [[ -n "$line" ]]; do
while read -r line || [[ -n "$line" ]]; do
if [[ -z "$line" ]] || [[ "$line" == "#"* ]]; then
continue
fi
@@ -702,6 +703,8 @@ release_and_deploy() {
source "$PLUGIN_AVAILABLE_PATH/config/functions"
local APP="$1"; local IMAGE_TAG="$2"; local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG")
local DOKKU_DOCKERFILE_PORTS
verify_app_name "$APP"
if verify_image "$IMAGE"; then
@@ -709,7 +712,7 @@ release_and_deploy() {
local IMAGE_SOURCE_TYPE="herokuish"
else
local IMAGE_SOURCE_TYPE="dockerfile"
local DOKKU_DOCKERFILE_PORTS=$(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)
DOKKU_DOCKERFILE_PORTS=$(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)
if [[ -z "$DOKKU_DOCKERFILE_PORTS" ]]; then
local DOCKER_IMAGE_PORTS=$(get_exposed_ports_from_image "$IMAGE")
[[ -n "$DOCKER_IMAGE_PORTS" ]] && config_set --no-restart "$APP" DOKKU_DOCKERFILE_PORTS="$DOCKER_IMAGE_PORTS"

8
plugins/common/glide.lock generated Normal file
View File

@@ -0,0 +1,8 @@
hash: e438ca8cf1bdca3e9c984bb1ee70c0d58be9da52cbe13a079a7c6cbc14210a37
updated: 2017-01-03T17:17:27.973373328-08:00
imports:
- name: github.com/codegangsta/inject
version: 33e0aa1cb7c019ccc3fbe049a8262a6403d30504
- name: github.com/codeskyblue/go-sh
version: ceb46ec4630a726eeed51d0a70066c9e1102c48f
testImports: []

View File

@@ -0,0 +1,3 @@
package: .
import:
- package: github.com/codeskyblue/go-sh

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core common plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -0,0 +1,2 @@
inject
inject.test

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Jeremy Saenz
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,92 @@
# inject
--
import "github.com/codegangsta/inject"
Package inject provides utilities for mapping and injecting dependencies in
various ways.
Language Translations:
* [简体中文](translations/README_zh_cn.md)
## Usage
#### func InterfaceOf
```go
func InterfaceOf(value interface{}) reflect.Type
```
InterfaceOf dereferences a pointer to an Interface type. It panics if value is
not an pointer to an interface.
#### type Applicator
```go
type Applicator interface {
// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'. Returns an error if the injection
// fails.
Apply(interface{}) error
}
```
Applicator represents an interface for mapping dependencies to a struct.
#### type Injector
```go
type Injector interface {
Applicator
Invoker
TypeMapper
// SetParent sets the parent of the injector. If the injector cannot find a
// dependency in its Type map it will check its parent before returning an
// error.
SetParent(Injector)
}
```
Injector represents an interface for mapping and injecting dependencies into
structs and function arguments.
#### func New
```go
func New() Injector
```
New returns a new Injector.
#### type Invoker
```go
type Invoker interface {
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type. Returns
// a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
Invoke(interface{}) ([]reflect.Value, error)
}
```
Invoker represents an interface for calling functions via reflection.
#### type TypeMapper
```go
type TypeMapper interface {
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
Map(interface{}) TypeMapper
// Maps the interface{} value based on the pointer of an Interface provided.
// This is really only useful for mapping a value as an interface, as interfaces
// cannot at this time be referenced directly without a pointer.
MapTo(interface{}, interface{}) TypeMapper
// Provides a possibility to directly insert a mapping based on type and value.
// This makes it possible to directly map type arguments not possible to instantiate
// with reflect like unidirectional channels.
Set(reflect.Type, reflect.Value) TypeMapper
// Returns the Value that is mapped to the current type. Returns a zeroed Value if
// the Type has not been mapped.
Get(reflect.Type) reflect.Value
}
```
TypeMapper represents an interface for mapping interface{} values based on type.

View File

@@ -0,0 +1,187 @@
// Package inject provides utilities for mapping and injecting dependencies in various ways.
package inject
import (
"fmt"
"reflect"
)
// Injector represents an interface for mapping and injecting dependencies into structs
// and function arguments.
type Injector interface {
Applicator
Invoker
TypeMapper
// SetParent sets the parent of the injector. If the injector cannot find a
// dependency in its Type map it will check its parent before returning an
// error.
SetParent(Injector)
}
// Applicator represents an interface for mapping dependencies to a struct.
type Applicator interface {
// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'. Returns an error if the injection
// fails.
Apply(interface{}) error
}
// Invoker represents an interface for calling functions via reflection.
type Invoker interface {
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type. Returns
// a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
Invoke(interface{}) ([]reflect.Value, error)
}
// TypeMapper represents an interface for mapping interface{} values based on type.
type TypeMapper interface {
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
Map(interface{}) TypeMapper
// Maps the interface{} value based on the pointer of an Interface provided.
// This is really only useful for mapping a value as an interface, as interfaces
// cannot at this time be referenced directly without a pointer.
MapTo(interface{}, interface{}) TypeMapper
// Provides a possibility to directly insert a mapping based on type and value.
// This makes it possible to directly map type arguments not possible to instantiate
// with reflect like unidirectional channels.
Set(reflect.Type, reflect.Value) TypeMapper
// Returns the Value that is mapped to the current type. Returns a zeroed Value if
// the Type has not been mapped.
Get(reflect.Type) reflect.Value
}
type injector struct {
values map[reflect.Type]reflect.Value
parent Injector
}
// InterfaceOf dereferences a pointer to an Interface type.
// It panics if value is not an pointer to an interface.
func InterfaceOf(value interface{}) reflect.Type {
t := reflect.TypeOf(value)
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Interface {
panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
}
return t
}
// New returns a new Injector.
func New() Injector {
return &injector{
values: make(map[reflect.Type]reflect.Value),
}
}
// Invoke attempts to call the interface{} provided as a function,
// providing dependencies for function arguments based on Type.
// Returns a slice of reflect.Value representing the returned values of the function.
// Returns an error if the injection fails.
// It panics if f is not a function
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
t := reflect.TypeOf(f)
var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
for i := 0; i < t.NumIn(); i++ {
argType := t.In(i)
val := inj.Get(argType)
if !val.IsValid() {
return nil, fmt.Errorf("Value not found for type %v", argType)
}
in[i] = val
}
return reflect.ValueOf(f).Call(in), nil
}
// Maps dependencies in the Type map to each field in the struct
// that is tagged with 'inject'.
// Returns an error if the injection fails.
func (inj *injector) Apply(val interface{}) error {
v := reflect.ValueOf(val)
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil // Should not panic here ?
}
t := v.Type()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
structField := t.Field(i)
if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") {
ft := f.Type()
v := inj.Get(ft)
if !v.IsValid() {
return fmt.Errorf("Value not found for type %v", ft)
}
f.Set(v)
}
}
return nil
}
// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
// It returns the TypeMapper registered in.
func (i *injector) Map(val interface{}) TypeMapper {
i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
return i
}
func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
return i
}
// Maps the given reflect.Type to the given reflect.Value and returns
// the Typemapper the mapping has been registered in.
func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
i.values[typ] = val
return i
}
func (i *injector) Get(t reflect.Type) reflect.Value {
val := i.values[t]
if val.IsValid() {
return val
}
// no concrete types found, try to find implementors
// if t is an interface
if t.Kind() == reflect.Interface {
for k, v := range i.values {
if k.Implements(t) {
val = v
break
}
}
}
// Still no type found, try to look it up on the parent
if !val.IsValid() && i.parent != nil {
val = i.parent.Get(t)
}
return val
}
func (i *injector) SetParent(parent Injector) {
i.parent = parent
}

View File

@@ -0,0 +1,159 @@
package inject_test
import (
"fmt"
"github.com/codegangsta/inject"
"reflect"
"testing"
)
type SpecialString interface {
}
type TestStruct struct {
Dep1 string `inject:"t" json:"-"`
Dep2 SpecialString `inject`
Dep3 string
}
type Greeter struct {
Name string
}
func (g *Greeter) String() string {
return "Hello, My name is" + g.Name
}
/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
if a != b {
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
func refute(t *testing.T, a interface{}, b interface{}) {
if a == b {
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
func Test_InjectorInvoke(t *testing.T) {
injector := inject.New()
expect(t, injector == nil, false)
dep := "some dependency"
injector.Map(dep)
dep2 := "another dep"
injector.MapTo(dep2, (*SpecialString)(nil))
dep3 := make(chan *SpecialString)
dep4 := make(chan *SpecialString)
typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem())
typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem())
injector.Set(typRecv, reflect.ValueOf(dep3))
injector.Set(typSend, reflect.ValueOf(dep4))
_, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) {
expect(t, d1, dep)
expect(t, d2, dep2)
expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem())
expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem())
expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir)
expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir)
})
expect(t, err, nil)
}
func Test_InjectorInvokeReturnValues(t *testing.T) {
injector := inject.New()
expect(t, injector == nil, false)
dep := "some dependency"
injector.Map(dep)
dep2 := "another dep"
injector.MapTo(dep2, (*SpecialString)(nil))
result, err := injector.Invoke(func(d1 string, d2 SpecialString) string {
expect(t, d1, dep)
expect(t, d2, dep2)
return "Hello world"
})
expect(t, result[0].String(), "Hello world")
expect(t, err, nil)
}
func Test_InjectorApply(t *testing.T) {
injector := inject.New()
injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil))
s := TestStruct{}
err := injector.Apply(&s)
expect(t, err, nil)
expect(t, s.Dep1, "a dep")
expect(t, s.Dep2, "another dep")
expect(t, s.Dep3, "")
}
func Test_InterfaceOf(t *testing.T) {
iType := inject.InterfaceOf((*SpecialString)(nil))
expect(t, iType.Kind(), reflect.Interface)
iType = inject.InterfaceOf((**SpecialString)(nil))
expect(t, iType.Kind(), reflect.Interface)
// Expecting nil
defer func() {
rec := recover()
refute(t, rec, nil)
}()
iType = inject.InterfaceOf((*testing.T)(nil))
}
func Test_InjectorSet(t *testing.T) {
injector := inject.New()
typ := reflect.TypeOf("string")
typSend := reflect.ChanOf(reflect.SendDir, typ)
typRecv := reflect.ChanOf(reflect.RecvDir, typ)
// instantiating unidirectional channels is not possible using reflect
// http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064
chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
injector.Set(typSend, chanSend)
injector.Set(typRecv, chanRecv)
expect(t, injector.Get(typSend).IsValid(), true)
expect(t, injector.Get(typRecv).IsValid(), true)
expect(t, injector.Get(chanSend.Type()).IsValid(), false)
}
func Test_InjectorGet(t *testing.T) {
injector := inject.New()
injector.Map("some dependency")
expect(t, injector.Get(reflect.TypeOf("string")).IsValid(), true)
expect(t, injector.Get(reflect.TypeOf(11)).IsValid(), false)
}
func Test_InjectorSetParent(t *testing.T) {
injector := inject.New()
injector.MapTo("another dep", (*SpecialString)(nil))
injector2 := inject.New()
injector2.SetParent(injector)
expect(t, injector2.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true)
}
func TestInjectImplementors(t *testing.T) {
injector := inject.New()
g := &Greeter{"Jeremy"}
injector.Map(g)
expect(t, injector.Get(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
}

View File

@@ -0,0 +1,85 @@
# inject
--
import "github.com/codegangsta/inject"
inject包提供了多种对实体的映射和依赖注入方式。
## 用法
#### func InterfaceOf
```go
func InterfaceOf(value interface{}) reflect.Type
```
函数InterfaceOf返回指向接口类型的指针。如果传入的value值不是指向接口的指针,将抛出一个panic异常。
#### type Applicator
```go
type Applicator interface {
// 在Type map中维持对结构体中每个域的引用并用'inject'来标记
// 如果注入失败将会返回一个error.
Apply(interface{}) error
}
```
Applicator接口表示到结构体的依赖映射关系。
#### type Injector
```go
type Injector interface {
Applicator
Invoker
TypeMapper
// SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖,
// 将会继续从它的父injector中找,直到返回error.
SetParent(Injector)
}
```
Injector接口表示对结构体、函数参数的映射和依赖注入。
#### func New
```go
func New() Injector
```
New创建并返回一个Injector.
#### type Invoker
```go
type Invoker interface {
// Invoke尝试将interface{}作为一个函数来调用,并基于Type为函数提供参数。
// 它将返回reflect.Value的切片,其中存放原函数的返回值。
// 如果注入失败则返回error.
Invoke(interface{}) ([]reflect.Value, error)
}
```
Invoker接口表示通过反射进行函数调用。
#### type TypeMapper
```go
type TypeMapper interface {
// 基于调用reflect.TypeOf得到的类型映射interface{}的值。
Map(interface{}) TypeMapper
// 基于提供的接口的指针映射interface{}的值。
// 该函数仅用来将一个值映射为接口,因为接口无法不通过指针而直接引用到。
MapTo(interface{}, interface{}) TypeMapper
// 为直接插入基于类型和值的map提供一种可能性。
// 它使得这一类直接映射成为可能:无法通过反射直接实例化的类型参数,如单向管道。
Set(reflect.Type, reflect.Value) TypeMapper
// 返回映射到当前类型的Value. 如果Type没被映射,将返回对应的零值。
Get(reflect.Type) reflect.Value
}
```
TypeMapper接口用来表示基于类型到接口值的映射。
## 译者
张强 (qqbunny@yeah.net)

View File

@@ -0,0 +1,3 @@
#!/bin/bash
go get github.com/robertkrimen/godocdown/godocdown
godocdown > README.md

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,69 @@
## OLD README
First give you a full example, I will explain every command below.
session := sh.NewSession()
session.Env["PATH"] = "/usr/bin:/bin"
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Alias("ll", "ls", "-l")
session.ShowCMD = true // enable for debug
var err error
err = session.Call("ll", "/")
if err != nil {
log.Fatal(err)
}
ret, err := session.Capture("pwd", sh.Dir("/home")) # wraper of session.Call
if err != nil {
log.Fatal(err)
}
# ret is "/home\n"
fmt.Println(ret)
create a new Session
session := sh.NewSession()
use alias like this
session.Alias("ll", "ls", "-l") # like alias ll='ls -l'
set current env like this
session.Env["BUILD_ID"] = "123" # like export BUILD_ID=123
set current directory
session.Set(sh.Dir("/")) # like cd /
pipe is also supported
session.Command("echo", "hello\tworld").Command("cut", "-f2")
// output should be "world"
session.Run()
test, the build in command support
session.Test("d", "dir") // test dir
session.Test("f", "file) // test regular file
with `Alias Env Set Call Capture Command` a shell scripts can be easily converted into golang program. below is a shell script.
#!/bin/bash -
#
export PATH=/usr/bin:/bin
alias ll='ls -l'
cd /usr
if test -d "local"
then
ll local | awk '{print $1, $NF}'
fi
convert to golang, will be
s := sh.NewSession()
s.Env["PATH"] = "/usr/bin:/bin"
s.Set(sh.Dir("/usr"))
s.Alias("ll", "ls", "-l")
if s.Test("d", "local") {
s.Command("ll", "local").Command("awk", "{print $1, $NF}").Run()
}

View File

@@ -0,0 +1,85 @@
## go-sh
[![wercker status](https://app.wercker.com/status/009acbd4f00ccc6de7e2554e12a50d84/s "wercker status")](https://app.wercker.com/project/bykey/009acbd4f00ccc6de7e2554e12a50d84)
[![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/codeskyblue/go-sh)
*If you depend on the old api, see tag: v.0.1*
install: `go get github.com/codeskyblue/go-sh`
Pipe Example:
package main
import "github.com/codeskyblue/go-sh"
func main() {
sh.Command("echo", "hello\tworld").Command("cut", "-f2").Run()
}
Because I like os/exec, `go-sh` is very much modelled after it. However, `go-sh` provides a better experience.
These are some of its features:
* keep the variable environment (e.g. export)
* alias support (e.g. alias in shell)
* remember current dir
* pipe command
* shell build-in commands echo & test
* timeout support
Examples are important:
sh: echo hello
go: sh.Command("echo", "hello").Run()
sh: export BUILD_ID=123
go: s = sh.NewSession().SetEnv("BUILD_ID", "123")
sh: alias ll='ls -l'
go: s = sh.NewSession().Alias('ll', 'ls', '-l')
sh: (cd /; pwd)
go: sh.Command("pwd", sh.Dir("/")).Run()
sh: test -d data || mkdir data
go: if ! sh.Test("dir", "data") { sh.Command("mkdir", "data").Run() }
sh: cat first second | awk '{print $1}'
go: sh.Command("cat", "first", "second").Command("awk", "{print $1}").Run()
sh: count=$(echo "one two three" | wc -w)
go: count, err := sh.Echo("one two three").Command("wc", "-w").Output()
sh(in ubuntu): timeout 1s sleep 3
go: c := sh.Command("sleep", "3"); c.Start(); c.WaitTimeout(time.Second) # default SIGKILL
go: out, err := sh.Command("sleep", "3").SetTimeout(time.Second).Output() # set session timeout and get output)
sh: echo hello | cat
go: out, err := sh.Command("cat").SetInput("hello").Output()
sh: cat # read from stdin
go: out, err := sh.Command("cat").SetStdin(os.Stdin).Output()
If you need to keep env and dir, it is better to create a session
session := sh.NewSession()
session.SetEnv("BUILD_ID", "123")
session.SetDir("/")
# then call cmd
session.Command("echo", "hello").Run()
# set ShowCMD to true for easily debug
session.ShowCMD = true
for more information, it better to see docs.
[![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/codeskyblue/go-sh)
### contribute
If you love this project, starring it will encourage the coder. Pull requests are welcome.
support the author: [alipay](https://me.alipay.com/goskyblue)
### thanks
this project is based on <http://github.com/codegangsta/inject>. thanks for the author.
# the reason to use Go shell
Sometimes we need to write shell scripts, but shell scripts are not good at working cross platform, Go, on the other hand, is good at that. Is there a good way to use Go to write shell like scripts? Using go-sh we can do this now.

View File

@@ -0,0 +1,41 @@
package main
import (
"fmt"
"log"
"github.com/codeskyblue/go-sh"
)
func main() {
sh.Command("echo", "hello").Run()
out, err := sh.Command("echo", "hello").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println("output is", string(out))
var a int
sh.Command("echo", "2").UnmarshalJSON(&a)
fmt.Println("a =", a)
s := sh.NewSession()
s.Alias("hi", "echo", "hi")
s.Command("hi", "boy").Run()
fmt.Print("pwd = ")
s.Command("pwd", sh.Dir("/")).Run()
if !sh.Test("dir", "data") {
sh.Command("echo", "mkdir", "data").Run()
}
sh.Command("echo", "hello", "world").
Command("awk", `{print "second arg is "$2}`).Run()
s.ShowCMD = true
s.Command("echo", "hello", "world").
Command("awk", `{print "second arg is "$2}`).Run()
s.SetEnv("BUILD_ID", "123").Command("bash", "-c", "echo $BUILD_ID").Run()
s.Command("bash", "-c", "echo current shell is $SHELL").Run()
}

View File

@@ -0,0 +1,7 @@
package main
import "github.com/codeskyblue/go-sh"
func main() {
sh.Command("less", "less.go").Run()
}

View File

@@ -0,0 +1,17 @@
package main
import (
"flag"
"fmt"
"github.com/codeskyblue/go-sh"
)
func main() {
flag.Parse()
if flag.NArg() != 1 {
fmt.Println("Usage: PROGRAM <file>")
return
}
sh.Command("tail", "-f", flag.Arg(0)).Run()
}

View File

@@ -0,0 +1,23 @@
package main
import (
"fmt"
"time"
sh "github.com/codeskyblue/go-sh"
)
func main() {
c := sh.Command("sleep", "3")
c.Start()
err := c.WaitTimeout(time.Second * 1)
if err != nil {
fmt.Printf("timeout should happend: %v\n", err)
}
// timeout should be a session
out, err := sh.Command("sleep", "2").SetTimeout(time.Second).Output()
fmt.Printf("output:(%s), err(%v)\n", string(out), err)
out, err = sh.Command("echo", "hello").SetTimeout(time.Second).Output()
fmt.Printf("output:(%s), err(%v)\n", string(out), err)
}

View File

@@ -0,0 +1,28 @@
package sh_test
import (
"fmt"
"github.com/codeskyblue/go-sh"
)
func ExampleCommand() {
out, err := sh.Command("echo", "hello").Output()
fmt.Println(string(out), err)
}
func ExampleCommandPipe() {
out, err := sh.Command("echo", "-n", "hi").Command("wc", "-c").Output()
fmt.Println(string(out), err)
}
func ExampleCommandSetDir() {
out, err := sh.Command("pwd", sh.Dir("/")).Output()
fmt.Println(string(out), err)
}
func ExampleTest() {
if sh.Test("dir", "mydir") {
fmt.Println("mydir exists")
}
}

View File

@@ -0,0 +1,148 @@
package sh
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"io"
"os"
"strings"
"syscall"
"time"
)
var ErrExecTimeout = errors.New("execute timeout")
// unmarshal shell output to decode json
func (s *Session) UnmarshalJSON(data interface{}) (err error) {
bufrw := bytes.NewBuffer(nil)
s.Stdout = bufrw
if err = s.Run(); err != nil {
return
}
return json.NewDecoder(bufrw).Decode(data)
}
// unmarshal command output into xml
func (s *Session) UnmarshalXML(data interface{}) (err error) {
bufrw := bytes.NewBuffer(nil)
s.Stdout = bufrw
if err = s.Run(); err != nil {
return
}
return xml.NewDecoder(bufrw).Decode(data)
}
// start command
func (s *Session) Start() (err error) {
s.started = true
var rd *io.PipeReader
var wr *io.PipeWriter
var length = len(s.cmds)
if s.ShowCMD {
var cmds = make([]string, 0, 4)
for _, cmd := range s.cmds {
cmds = append(cmds, strings.Join(cmd.Args, " "))
}
s.writePrompt(strings.Join(cmds, " | "))
}
for index, cmd := range s.cmds {
if index == 0 {
cmd.Stdin = s.Stdin
} else {
cmd.Stdin = rd
}
if index != length {
rd, wr = io.Pipe() // create pipe
cmd.Stdout = wr
cmd.Stderr = os.Stderr
}
if index == length-1 {
cmd.Stdout = s.Stdout
cmd.Stderr = s.Stderr
}
err = cmd.Start()
if err != nil {
return
}
}
return
}
// Should be call after Start()
// only catch the last command error
func (s *Session) Wait() (err error) {
for _, cmd := range s.cmds {
err = cmd.Wait()
wr, ok := cmd.Stdout.(*io.PipeWriter)
if ok {
wr.Close()
}
}
return err
}
func (s *Session) Kill(sig os.Signal) {
for _, cmd := range s.cmds {
if cmd.Process != nil {
cmd.Process.Signal(sig)
}
}
}
func (s *Session) WaitTimeout(timeout time.Duration) (err error) {
select {
case <-time.After(timeout):
s.Kill(syscall.SIGKILL)
return ErrExecTimeout
case err = <-Go(s.Wait):
return err
}
}
func Go(f func() error) chan error {
ch := make(chan error)
go func() {
ch <- f()
}()
return ch
}
func (s *Session) Run() (err error) {
if err = s.Start(); err != nil {
return
}
if s.timeout != time.Duration(0) {
return s.WaitTimeout(s.timeout)
}
return s.Wait()
}
func (s *Session) Output() (out []byte, err error) {
oldout := s.Stdout
defer func() {
s.Stdout = oldout
}()
stdout := bytes.NewBuffer(nil)
s.Stdout = stdout
err = s.Run()
out = stdout.Bytes()
return
}
func (s *Session) CombinedOutput() (out []byte, err error) {
oldout := s.Stdout
olderr := s.Stderr
defer func() {
s.Stdout = oldout
s.Stderr = olderr
}()
stdout := bytes.NewBuffer(nil)
s.Stdout = stdout
s.Stderr = stdout
err = s.Run()
out = stdout.Bytes()
return
}

View File

@@ -0,0 +1,126 @@
package sh
import (
"encoding/xml"
"io"
"os"
"os/exec"
"strings"
"testing"
"time"
)
func TestUnmarshalJSON(t *testing.T) {
var a int
s := NewSession()
s.ShowCMD = true
err := s.Command("echo", []string{"1"}).UnmarshalJSON(&a)
if err != nil {
t.Error(err)
}
if a != 1 {
t.Errorf("expect a tobe 1, but got %d", a)
}
}
func TestUnmarshalXML(t *testing.T) {
s := NewSession()
xmlSample := `<?xml version="1.0" encoding="utf-8"?>
<server version="1" />`
type server struct {
XMLName xml.Name `xml:"server"`
Version string `xml:"version,attr"`
}
data := &server{}
s.Command("echo", xmlSample).UnmarshalXML(data)
if data.Version != "1" {
t.Error(data)
}
}
func TestPipe(t *testing.T) {
s := NewSession()
s.ShowCMD = true
s.Call("echo", "hello")
err := s.Command("echo", "hi").Command("cat", "-n").Start()
if err != nil {
t.Error(err)
}
err = s.Wait()
if err != nil {
t.Error(err)
}
out, err := s.Command("echo", []string{"hello"}).Output()
if err != nil {
t.Error(err)
}
if string(out) != "hello\n" {
t.Error("capture wrong output:", out)
}
s.Command("echo", []string{"hello\tworld"}).Command("cut", []string{"-f2"}).Run()
}
func TestPipeCommand(t *testing.T) {
c1 := exec.Command("echo", "good")
rd, wr := io.Pipe()
c1.Stdout = wr
c2 := exec.Command("cat", "-n")
c2.Stdout = os.Stdout
c2.Stdin = rd
c1.Start()
c2.Start()
c1.Wait()
wc, ok := c1.Stdout.(io.WriteCloser)
if ok {
wc.Close()
}
c2.Wait()
}
func TestPipeInput(t *testing.T) {
s := NewSession()
s.ShowCMD = true
s.SetInput("first line\nsecond line\n")
out, err := s.Command("grep", "second").Output()
if err != nil {
t.Error(err)
}
if string(out) != "second line\n" {
t.Error("capture wrong output:", out)
}
}
func TestTimeout(t *testing.T) {
s := NewSession()
err := s.Command("sleep", "2").Start()
if err != nil {
t.Fatal(err)
}
err = s.WaitTimeout(time.Second)
if err != ErrExecTimeout {
t.Fatal(err)
}
}
func TestSetTimeout(t *testing.T) {
s := NewSession()
s.SetTimeout(time.Second)
defer s.SetTimeout(0)
err := s.Command("sleep", "2").Run()
if err != ErrExecTimeout {
t.Fatal(err)
}
}
func TestCombinedOutput(t *testing.T) {
s := NewSession()
bytes, err := s.Command("sh", "-c", "echo stderr >&2 ; echo stdout").CombinedOutput()
if err != nil {
t.Error(err)
}
stringOutput := string(bytes)
if !(strings.Contains(stringOutput, "stdout") && strings.Contains(stringOutput, "stderr")) {
t.Errorf("expect output from both output streams, got '%s'", strings.TrimSpace(stringOutput))
}
}

View File

@@ -0,0 +1,200 @@
/*
Package go-sh is intented to make shell call with golang more easily.
Some usage is more similar to os/exec, eg: Run(), Output(), Command(name, args...)
But with these similar function, pipe is added in and this package also got shell-session support.
Why I love golang so much, because the usage of golang is simple, but the power is unlimited. I want to make this pakcage got the sample style like golang.
// just like os/exec
sh.Command("echo", "hello").Run()
// support pipe
sh.Command("echo", "hello").Command("wc", "-c").Run()
// create a session to store dir and env
sh.NewSession().SetDir("/").Command("pwd")
// shell buildin command - "test"
sh.Test("dir", "mydir")
// like shell call: (cd /; pwd)
sh.Command("pwd", sh.Dir("/")) same with sh.Command(sh.Dir("/"), "pwd")
// output to json and xml easily
v := map[string] int {}
err = sh.Command("echo", `{"number": 1}`).UnmarshalJSON(&v)
*/
package sh
import (
"fmt"
"io"
"os"
"os/exec"
"reflect"
"strings"
"time"
"github.com/codegangsta/inject"
)
type Dir string
type Session struct {
inj inject.Injector
alias map[string][]string
cmds []*exec.Cmd
dir Dir
started bool
Env map[string]string
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
ShowCMD bool // enable for debug
timeout time.Duration
}
func (s *Session) writePrompt(args ...interface{}) {
var ps1 = fmt.Sprintf("[golang-sh]$")
args = append([]interface{}{ps1}, args...)
fmt.Fprintln(s.Stderr, args...)
}
func NewSession() *Session {
env := make(map[string]string)
for _, key := range []string{"PATH"} {
env[key] = os.Getenv(key)
}
s := &Session{
inj: inject.New(),
alias: make(map[string][]string),
dir: Dir(""),
Stdin: strings.NewReader(""),
Stdout: os.Stdout,
Stderr: os.Stderr,
Env: env,
}
return s
}
func InteractiveSession() *Session {
s := NewSession()
s.SetStdin(os.Stdin)
return s
}
func Command(name string, a ...interface{}) *Session {
s := NewSession()
return s.Command(name, a...)
}
func Echo(in string) *Session {
s := NewSession()
return s.SetInput(in)
}
func (s *Session) Alias(alias, cmd string, args ...string) {
v := []string{cmd}
v = append(v, args...)
s.alias[alias] = v
}
func (s *Session) Command(name string, a ...interface{}) *Session {
var args = make([]string, 0)
var sType = reflect.TypeOf("")
// init cmd, args, dir, envs
// if not init, program may panic
s.inj.Map(name).Map(args).Map(s.dir).Map(map[string]string{})
for _, v := range a {
switch reflect.TypeOf(v) {
case sType:
args = append(args, v.(string))
default:
s.inj.Map(v)
}
}
if len(args) != 0 {
s.inj.Map(args)
}
s.inj.Invoke(s.appendCmd)
return s
}
// combine Command and Run
func (s *Session) Call(name string, a ...interface{}) error {
return s.Command(name, a...).Run()
}
/*
func (s *Session) Exec(cmd string, args ...string) error {
return s.Call(cmd, args)
}
*/
func (s *Session) SetEnv(key, value string) *Session {
s.Env[key] = value
return s
}
func (s *Session) SetDir(dir string) *Session {
s.dir = Dir(dir)
return s
}
func (s *Session) SetInput(in string) *Session {
s.Stdin = strings.NewReader(in)
return s
}
func (s *Session) SetStdin(r io.Reader) *Session {
s.Stdin = r
return s
}
func (s *Session) SetTimeout(d time.Duration) *Session {
s.timeout = d
return s
}
func newEnviron(env map[string]string, inherit bool) []string { //map[string]string {
environ := make([]string, 0, len(env))
if inherit {
for _, line := range os.Environ() {
for k, _ := range env {
if strings.HasPrefix(line, k+"=") {
goto CONTINUE
}
}
environ = append(environ, line)
CONTINUE:
}
}
for k, v := range env {
environ = append(environ, k+"="+v)
}
return environ
}
func (s *Session) appendCmd(cmd string, args []string, cwd Dir, env map[string]string) {
if s.started {
s.started = false
s.cmds = make([]*exec.Cmd, 0)
}
for k, v := range s.Env {
if _, ok := env[k]; !ok {
env[k] = v
}
}
environ := newEnviron(s.Env, true) // true: inherit sys-env
v, ok := s.alias[cmd]
if ok {
cmd = v[0]
args = append(v[1:], args...)
}
c := exec.Command(cmd, args...)
c.Env = environ
c.Dir = string(cwd)
s.cmds = append(s.cmds, c)
}

View File

@@ -0,0 +1,106 @@
package sh
import (
"fmt"
"log"
"runtime"
"strings"
"testing"
)
func TestAlias(t *testing.T) {
s := NewSession()
s.Alias("gr", "echo", "hi")
out, err := s.Command("gr", "sky").Output()
if err != nil {
t.Error(err)
}
if string(out) != "hi sky\n" {
t.Errorf("expect 'hi sky' but got:%s", string(out))
}
}
func ExampleSession_Command() {
s := NewSession()
out, err := s.Command("echo", "hello").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
// Output: hello
}
func ExampleSession_Command_pipe() {
s := NewSession()
out, err := s.Command("echo", "hello", "world").Command("awk", "{print $2}").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
// Output: world
}
func ExampleSession_Alias() {
s := NewSession()
s.Alias("alias_echo_hello", "echo", "hello")
out, err := s.Command("alias_echo_hello", "world").Output()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(out))
// Output: hello world
}
func TestEcho(t *testing.T) {
out, err := Echo("one two three").Command("wc", "-w").Output()
if err != nil {
t.Error(err)
}
if strings.TrimSpace(string(out)) != "3" {
t.Errorf("expect '3' but got:%s", string(out))
}
}
func TestSession(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("ignore test on windows")
return
}
session := NewSession()
session.ShowCMD = true
err := session.Call("pwd")
if err != nil {
t.Error(err)
}
out, err := session.SetDir("/").Command("pwd").Output()
if err != nil {
t.Error(err)
}
if string(out) != "/\n" {
t.Errorf("expect /, but got %s", string(out))
}
}
/*
#!/bin/bash -
#
export PATH=/usr/bin:/bin
alias ll='ls -l'
cd /usr
if test -d "local"
then
ll local | awk '{print $1, $NF}' | grep bin
fi
*/
func Example(t *testing.T) {
s := NewSession()
//s.ShowCMD = true
s.Env["PATH"] = "/usr/bin:/bin"
s.SetDir("/bin")
s.Alias("ll", "ls", "-l")
if s.Test("d", "local") {
//s.Command("ll", []string{"local"}).Command("awk", []string{"{print $1, $NF}"}).Command("grep", []string{"bin"}).Run()
s.Command("ll", "local").Command("awk", "{print $1, $NF}").Command("grep", "bin").Run()
}
}

View File

@@ -0,0 +1,64 @@
package sh
import (
"os"
"path/filepath"
)
func filetest(name string, modemask os.FileMode) (match bool, err error) {
fi, err := os.Stat(name)
if err != nil {
return
}
match = (fi.Mode() & modemask) == modemask
return
}
func (s *Session) pwd() string {
dir := string(s.dir)
if dir == "" {
dir, _ = os.Getwd()
}
return dir
}
func (s *Session) abspath(name string) string {
if filepath.IsAbs(name) {
return name
}
return filepath.Join(s.pwd(), name)
}
func init() {
//log.SetFlags(log.Lshortfile | log.LstdFlags)
}
// expression can be dir, file, link
func (s *Session) Test(expression string, argument string) bool {
var err error
var fi os.FileInfo
fi, err = os.Lstat(s.abspath(argument))
switch expression {
case "d", "dir":
return err == nil && fi.IsDir()
case "f", "file":
return err == nil && fi.Mode().IsRegular()
case "x", "executable":
/*
fmt.Println(expression, argument)
if err == nil {
fmt.Println(fi.Mode())
}
*/
return err == nil && fi.Mode()&os.FileMode(0100) != 0
case "L", "link":
return err == nil && fi.Mode()&os.ModeSymlink != 0
}
return false
}
// expression can be d,dir, f,file, link
func Test(exp string, arg string) bool {
s := NewSession()
return s.Test(exp, arg)
}

View File

@@ -0,0 +1,58 @@
package sh_test
import (
"testing"
"github.com/codeskyblue/go-sh"
)
var s = sh.NewSession()
type T struct{ *testing.T }
func NewT(t *testing.T) *T {
return &T{t}
}
func (t *T) checkTest(exp string, arg string, result bool) {
r := s.Test(exp, arg)
if r != result {
t.Errorf("test -%s %s, %v != %v", exp, arg, r, result)
}
}
func TestTest(i *testing.T) {
t := NewT(i)
t.checkTest("d", "../go-sh", true)
t.checkTest("d", "./yymm", false)
// file test
t.checkTest("f", "testdata/hello.txt", true)
t.checkTest("f", "testdata/xxxxx", false)
t.checkTest("f", "testdata/yymm", false)
// link test
t.checkTest("link", "testdata/linkfile", true)
t.checkTest("link", "testdata/xxxxxlinkfile", false)
t.checkTest("link", "testdata/hello.txt", false)
// executable test
t.checkTest("x", "testdata/executable", true)
t.checkTest("x", "testdata/xxxxx", false)
t.checkTest("x", "testdata/hello.txt", false)
}
func ExampleShellTest(t *testing.T) {
// test -L
sh.Test("link", "testdata/linkfile")
sh.Test("L", "testdata/linkfile")
// test -f
sh.Test("file", "testdata/file")
sh.Test("f", "testdata/file")
// test -x
sh.Test("executable", "testdata/binfile")
sh.Test("x", "testdata/binfile")
// test -d
sh.Test("dir", "testdata/dir")
sh.Test("d", "testdata/dir")
}

View File

View File

View File

@@ -0,0 +1 @@
hello.txt

View File

@@ -0,0 +1,28 @@
box: wercker/golang
# Build definition
build:
# The steps that will be executed on build
steps:
# Sets the go workspace and places you package
# at the right place in the workspace tree
- setup-go-workspace
# Gets the dependencies
- script:
name: go get
code: |
cd $WERCKER_SOURCE_DIR
go version
go get -t .
# Build the project
- script:
name: go build
code: |
go build .
# Test the project
- script:
name: go test
code: |
go test -v ./...

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core config plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core docker-options plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -10,6 +10,7 @@ docker_options_remove_cmd() {
verify_app_name "$2" && local APP="$2"
read -ra passed_phases <<< "$(get_phases "$3")"
shift 3 # everything else passed is the docker option
# shellcheck disable=SC2154
[[ -z ${passed_docker_option="$@"} ]] && dokku_log_fail "Please specify docker options to add to the phase"
remove_passed_docker_option passed_phases[@] "${passed_docker_option[@]}"
}

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core domains plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core enter plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -12,7 +12,7 @@ enter_default_cmd() {
if [[ -z "$3" ]]; then
if [[ ${#AVAILABLE_CONTAINER_TYPES[@]} -gt 1 ]]; then
dokku_log_warn "No container type specified."
dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[@]}"
dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[*]}"
else
CONTAINER_TYPE="${AVAILABLE_CONTAINER_TYPES[0]}"
fi
@@ -22,11 +22,11 @@ enter_default_cmd() {
local DOKKU_APP_CIDS=( $(get_app_container_ids "$APP") )
if [[ ! -n "$4" ]]; then
dokku_log_warn "No container id specified."
dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[@]}"
dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[*]}"
fi
if ! (printf -- '%s\n' "${DOKKU_APP_CIDS[@]}" | grep -q -e "^$4"); then
dokku_log_warn "Invalid container id for app"
dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[@]}"
dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[*]}"
fi
local ID=$(printf -- '%s\n' "${DOKKU_APP_CIDS[@]}" | grep -e "^$4")
shift 4
@@ -35,7 +35,7 @@ enter_default_cmd() {
local ID=${DOKKU_APP_CIDS[0]}
if [[ ! -n $ID ]]; then
dokku_log_warn "No containers found for type '$CONTAINER_TYPE'"
dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[@]}"
dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[*]}"
fi
if [[ $3 ]]; then
shift 3

View File

@@ -4,6 +4,23 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/apps/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions"
use_git_worktree() {
declare desc="detects whether to use git worktree"
local GIT_VERSION MAJOR_VERSION MINOR_VERSION
GIT_VERSION=$(git --version | awk '{split($0,a," "); print a[3]}')
MAJOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[1]}')
MINOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[2]}')
if [[ "$MAJOR_VERSION" -ge "3" ]]; then
return 0
elif [[ "$MAJOR_VERSION" -eq "2" ]] && [[ "$MINOR_VERSION" -ge "11" ]]; then
return 0
else
return 1
fi
}
git_build_app_repo() {
declare desc="builds local git app repo for app"
verify_app_name "$1"
@@ -13,26 +30,37 @@ git_build_app_repo() {
local GIT_BUILD_APP_REPO_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku_git.XXXX")
trap 'rm -rf "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null' RETURN INT TERM EXIT
# git clone - this method creates a new git repository and adds the primary
# repo as a remote, then does a fetch depth=1 to avoid cloning
# the entire repo
local TMP_TAG="dokku/$REV"
chmod 755 "$GIT_BUILD_APP_REPO_TMP_WORK_DIR"
unset GIT_DIR GIT_WORK_TREE
pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
[[ ! -d "$DOKKU_ROOT/$APP" ]] && apps_create "$APP"
GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
GIT_DIR="$DOKKU_ROOT/$APP" git tag "$TMP_TAG" "$REV" &> /dev/null
git init &> /dev/null
git config advice.detachedHead false
git remote add origin "$DOKKU_ROOT/$APP" &> /dev/null
git fetch --depth=1 origin "refs/tags/$TMP_TAG" &> /dev/null
git reset --hard FETCH_HEAD &> /dev/null
suppress_output git submodule update --init --recursive
GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
find -name .git -prune -exec rm -rf {} \; > /dev/null
plugn trigger post-extract "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV"
if use_git_worktree; then
# git worktree - this method uses git worktree which was introduced in git 2.5
git worktree add "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" > /dev/null
pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
else
# git clone - this method creates a new git repository and adds the primary
# repo as a remote, then does a fetch depth=1 to avoid cloning
# the entire repo.
# Not working for git >= 2.11.0 due to changes introduced
# in this merge:
# https://github.com/git/git/commit/25ab004c53cdcfea485e5bf437aeaa74df47196d
pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
GIT_DIR="$DOKKU_ROOT/$APP" git tag "$TMP_TAG" "$REV" &> /dev/null
git init &> /dev/null
git config advice.detachedHead false
git remote add origin "$DOKKU_ROOT/$APP" &> /dev/null
git fetch --depth=1 origin "refs/tags/$TMP_TAG" &> /dev/null
git reset --hard FETCH_HEAD &> /dev/null
GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
fi
suppress_output git submodule update --init --recursive
find . -name .git -prune -exec rm -rf {} \; > /dev/null
plugn trigger post-extract "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" | sed -u "s/^/"$'\e[1G'"/"
if [[ -f Dockerfile ]] && [[ "$([[ -f .env ]] && grep -q BUILDPACK_URL .env; echo $?)" != "0" ]] && [[ ! -f ".buildpacks" ]] && [[ -z $(config_get "$APP" BUILDPACK_URL || true) ]]; then
plugn trigger pre-receive-app "$APP" "dockerfile" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV"
@@ -79,7 +107,7 @@ git_hook_cmd() {
local APP="$2"
local DOKKU_DEPLOY_BRANCH="$(git_deploy_branch "$APP")"
if ! git check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" 2> /dev/null; then
if ! git check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" > /dev/null 2>&1; then
echo $'\e[1G\e[K'"-----> WARNING: Invalid branch name '$DOKKU_DEPLOY_BRANCH' specified via DOKKU_DEPLOY_BRANCH."
echo $'\e[1G\e[K'"-----> For more details, please see the man page for 'git-check-ref-format.'"
return

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core git plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core logs plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -17,7 +17,8 @@ logs_default_cmd() {
shift 2;
local TEMP=$(getopt -o htqn:p: --long help,tail,quiet,num:,ps: -n 'dokku logs' -- "$@")
if [[ $? != 0 ]]; then usage >&2 ; exit 1 ; fi
local EXIT_CODE="$?"
if [[ "$EXIT_CODE" != 0 ]]; then usage >&2 ; exit 1 ; fi
eval set -- "$TEMP"
local DOKKU_LOGS_LINE_NUMBERS="100"

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core named containers plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -28,6 +28,8 @@ chmod 0440 /etc/sudoers.d/dokku-nginx
# if dokku.conf has not been created, create it
if [[ ! -f /etc/nginx/conf.d/dokku.conf ]]; then
mkdir -p /etc/nginx/conf.d
chown root:root /etc/nginx/conf.d
cat<<EOF > /etc/nginx/conf.d/dokku.conf
include $DOKKU_ROOT/*/nginx.conf;

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core nginx-vhosts plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -38,8 +38,8 @@ server {
}
{{ else if eq $scheme "https"}}
server {
listen [::]:{{ $listen_port }} ssl {{ if eq $.SPDY_SUPPORTED "true" }}spdy{{ else if eq $.HTTP2_SUPPORTED "true" }}http2{{ end }};
listen {{ $listen_port }} ssl {{ if eq $.SPDY_SUPPORTED "true" }}spdy{{ else if eq $.HTTP2_SUPPORTED "true" }}http2{{ end }};
listen [::]:{{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }};
listen {{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }};
{{ if $.SSL_SERVER_NAME }}server_name {{ $.SSL_SERVER_NAME }}; {{ end }}
{{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }}
access_log /var/log/nginx/{{ $.APP }}-access.log;
@@ -50,7 +50,7 @@ server {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
keepalive_timeout 70;
{{ if eq $.SPDY_SUPPORTED "true" }}add_header Alternate-Protocol {{ $.NGINX_SSL_PORT }}:npn-spdy/2;{{ end }}
{{ if and (eq $.SPDY_SUPPORTED "true") (ne $.HTTP2_SUPPORTED "true") }}add_header Alternate-Protocol {{ $.NGINX_SSL_PORT }}:npn-spdy/2;{{ end }}
location / {

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core plugin plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core proxy plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -77,9 +77,9 @@ set_scale() {
for procscale in "${SCALE_SETTINGS[@]}"; do
local PROC_NAME=${procscale%%=*}
local PROC_COUNT=${procscale#*=}
is_number $PROC_COUNT || dokku_log_fail "ps:scale $PROC_COUNT is not a number"
is_number "$PROC_COUNT" || dokku_log_fail "ps:scale $PROC_COUNT is not a number"
dokku_log_info1_quiet "Scaling $APP:$PROC_NAME to $PROC_COUNT"
if (egrep -q ^${PROC_NAME}= "$DOKKU_SCALE_FILE" > /dev/null 2>&1); then
if (egrep -q "^${PROC_NAME}=" "$DOKKU_SCALE_FILE" > /dev/null 2>&1); then
sed --in-place "s:^${PROC_NAME}=.*:$PROC_NAME=$PROC_COUNT:g" "$DOKKU_SCALE_FILE"
else
echo "$PROC_NAME=$PROC_COUNT" >> "$DOKKU_SCALE_FILE"

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core ps plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

2
plugins/repo/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/commands
/subcommands/*

26
plugins/repo/Makefile Normal file
View File

@@ -0,0 +1,26 @@
include ../../common.mk
build-in-docker: clean
docker run --rm \
-v $$PWD/../..:$(GO_REPO_ROOT) \
-w $(GO_REPO_ROOT)/plugins/repo \
$(BUILD_IMAGE) \
bash -c "make build" || exit $$?
build: commands subcommands
subcommands: subcommands/gc subcommands/purge-cache
commands: **/**/commands.go
go build -a -o commands src/commands/commands.go
subcommands/gc: **/**/**/gc.go
go build -a -o subcommands/gc src/subcommands/gc/gc.go
subcommands/purge-cache: **/**/**/purge-cache.go
go build -a -o subcommands/purge-cache src/subcommands/purge-cache/purge-cache.go
clean:
rm -rf commands subcommands
src-clean:
rm -rf .gitignore src vendor Makefile

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
[[ " help repo:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
source "$PLUGIN_AVAILABLE_PATH/repo/internal-functions"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | repo:help)
repo_help_cmd "$@"
;;
*)
exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
;;
esac

View File

@@ -1,27 +0,0 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
repo_help_content_func () {
declare desc="return repo plugin help content"
cat<<help_content
repo:gc <app>, Runs 'git gc --aggressive' against the application's repo
repo:purge-cache <app>, Deletes the contents of the build cache stored in the repository
help_content
}
repo_help_cmd() {
if [[ $1 = "repo:help" ]] ; then
echo -e 'Usage: dokku repo[:COMMAND]'
echo ''
echo "Runs commands that interact with the app's repo"
echo ''
echo 'Additional commands:'
repo_help_content_func | sort | column -c2 -t -s,
elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
repo_help_content_func
else
cat<<help_desc
repo, Runs commands that interact with the app's repo
help_desc
fi
}

View File

@@ -1,4 +1,4 @@
[plugin]
description = "dokku core repo plugin"
version = "0.8.2"
version = "0.9.4"
[plugin.config]

View File

@@ -0,0 +1,54 @@
package main
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
columnize "github.com/ryanuber/columnize"
)
const (
helpHeader = `Usage: dokku repo[:COMMAND]
Runs commands that interact with the app's repo
Additional commands:`
helpContent = `
repo:gc <app>, Runs 'git gc --aggressive' against the application's repo
repo:purge-cache <app>, Deletes the contents of the build cache stored in the repository
`
)
func main() {
flag.Usage = usage
flag.Parse()
cmd := flag.Arg(0)
switch cmd {
case "repo:help":
usage()
case "help":
fmt.Print(helpContent)
default:
dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT"))
if err != nil {
fmt.Println("failed to retrieve DOKKU_NOT_IMPLEMENTED_EXIT environment variable")
dokkuNotImplementExitCode = 10
}
os.Exit(dokkuNotImplementExitCode)
}
}
func usage() {
config := columnize.DefaultConfig()
config.Delim = ","
config.Prefix = "\t"
config.Empty = ""
content := strings.Split(helpContent, "\n")[1:]
fmt.Println(helpHeader)
fmt.Println(columnize.Format(content, config))
}

6
plugins/repo/src/glide.lock generated Normal file
View File

@@ -0,0 +1,6 @@
hash: 1ddab5de41d1514c2722bd7e24758ad4b60bf6956eb5b9b925fa071a1427f149
updated: 2017-01-03T17:16:50.97156327-08:00
imports:
- name: github.com/ryanuber/columnize
version: 0fbbb3f0e3fbdc5bae7c6cd5f6c1887ebfb76360
testImports: []

View File

@@ -0,0 +1,3 @@
package: .
import:
- package: github.com/ryanuber/columnize

Some files were not shown because too many files have changed in this diff Show More