dante 0.1.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +30 -0
- data/.github/workflows/release.yml +210 -0
- data/CHANGELOG.md +96 -0
- data/Gemfile +1 -1
- data/LICENSE +2 -2
- data/README.md +23 -3
- data/Rakefile +15 -7
- data/dante.gemspec +4 -5
- data/lib/dante/runner.rb +186 -45
- data/lib/dante/version.rb +1 -1
- data/test/dante_test.rb +3 -3
- data/test/runner_test.rb +683 -57
- data/test/test_helper.rb +142 -12
- data/test/user_switch_test.rb +194 -0
- metadata +52 -85
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: aa7b32fa2eed7d038ee4835edd3155967b25f62360ba553c1f153004c8ccf0f6
|
|
4
|
+
data.tar.gz: 8e4d92c8ade70fd93d399744324de5205dbdb8c9f3b4c25a4b2445d0857a56d8
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 97423c3af18736da37830316eee8ade12cf3d1205ec158a70743a65ceb99e9df9f6268ff7e3946aa66808e93eb7c2c38550ee647c8dff874dd796aa4b815fa7c
|
|
7
|
+
data.tar.gz: 30d083e38a48c5a6d64c9a0af91a441c577199b833c38176d0bc4f470b1fd7a2861d6fa95f998c9f690203e6e039f1670abc73939db5c0fcc7b383112eee48b4
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "master" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "master" ]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3', '3.4', '4.0']
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- name: Set up Ruby
|
|
23
|
+
uses: ruby/setup-ruby@v1
|
|
24
|
+
with:
|
|
25
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
26
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: bundle exec rake test
|
|
29
|
+
- name: Run the user and group switching tests as root
|
|
30
|
+
run: sudo -E env "PATH=$PATH" bundle exec ruby -Ilib -Itest test/user_switch_test.rb
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Publishes a dante release to rubygems.org with trusted publishing, gated by the "release"
|
|
2
|
+
# environment: the owner approves each publish.
|
|
3
|
+
#
|
|
4
|
+
# Usual path: merge the release PR (the version in lib/dante/version.rb and the CHANGELOG.md heading
|
|
5
|
+
# stamped with the date, e.g. "## 0.3.0 (2026-11-01)"), then "Run workflow" on master with the
|
|
6
|
+
# version. build checks the version, that the changelog's first heading is that version with a date,
|
|
7
|
+
# and the branch, and builds the gem reproducibly; publish waits for approval, pushes the gem with
|
|
8
|
+
# short-lived trusted-publishing credentials, then creates the tag vX.Y.Z on the built commit. Pushing
|
|
9
|
+
# a vX.Y.Z tag on a master commit runs the same checks. "Re-run failed jobs" (not "Re-run all jobs",
|
|
10
|
+
# which rebuilds) resumes a release that stopped partway.
|
|
11
|
+
# If the gem is pushed but the tag step keeps failing, push the tag by hand at the built commit: that
|
|
12
|
+
# tag push's run skips the push (same sum).
|
|
13
|
+
#
|
|
14
|
+
# Dry run: tick dry_run to check the wiring. Everything runs except the changelog checks, the tag and
|
|
15
|
+
# the push; the credential exchange fails unless the trusted publisher is set up.
|
|
16
|
+
#
|
|
17
|
+
# The "release" environment's protection rules are the ONLY gate on publishing (required reviewer, no
|
|
18
|
+
# admin bypass, deployments only from master and v*.*.* tags): RubyGems trusts any run of this file
|
|
19
|
+
# that uses that environment, and a branch or tag can carry its own modified copy of it. The trusted
|
|
20
|
+
# publisher on rubygems.org must name the environment. Before approving a deployment, check
|
|
21
|
+
# who started the run, the event, that the commit is the release PR's merge commit, and the build
|
|
22
|
+
# log's "is on master" line. Reject any run you won't approve: a waiting run blocks the next one.
|
|
23
|
+
# A deployment rule for X.Y-stable lets anyone with write access create such a branch carrying its own
|
|
24
|
+
# copy of this file: add one only for a maintenance release, and remove it after.
|
|
25
|
+
|
|
26
|
+
name: release
|
|
27
|
+
run-name: release ${{ inputs.version || github.ref_name }}${{ inputs.dry_run && ' (dry run)' || '' }}
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
push:
|
|
31
|
+
tags: ['v[0-9]+.[0-9]+.[0-9]+']
|
|
32
|
+
workflow_dispatch:
|
|
33
|
+
inputs:
|
|
34
|
+
version:
|
|
35
|
+
description: 'Version to release (already in the version file of this branch), e.g. 1.2.0'
|
|
36
|
+
required: true
|
|
37
|
+
type: string
|
|
38
|
+
dry_run:
|
|
39
|
+
description: 'Dry run: check, build and exchange credentials, but create no tag and push nothing'
|
|
40
|
+
required: false
|
|
41
|
+
type: boolean
|
|
42
|
+
default: false
|
|
43
|
+
|
|
44
|
+
permissions:
|
|
45
|
+
contents: read
|
|
46
|
+
|
|
47
|
+
concurrency:
|
|
48
|
+
group: release
|
|
49
|
+
cancel-in-progress: false
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
build:
|
|
53
|
+
if: github.repository == 'nesquena/dante'
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
env:
|
|
56
|
+
TRUNK: master
|
|
57
|
+
TAG_PREFIX: v
|
|
58
|
+
GEMS: 'dante:.'
|
|
59
|
+
VERSION_FILE: lib/dante/version.rb
|
|
60
|
+
CHANGELOG: CHANGELOG.md
|
|
61
|
+
CHANGELOG_HEADING: '^## '
|
|
62
|
+
CHANGELOG_TOP: '^## ([0-9]+\.[0-9]+\.[0-9]+)( |$)'
|
|
63
|
+
outputs:
|
|
64
|
+
version: ${{ steps.check.outputs.version }}
|
|
65
|
+
tag: ${{ steps.check.outputs.tag }}
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
68
|
+
with:
|
|
69
|
+
fetch-depth: 0
|
|
70
|
+
submodules: true
|
|
71
|
+
persist-credentials: false
|
|
72
|
+
- uses: ruby/setup-ruby@de492cf36cbab201753d720ce4f4e336cd688c94 # v1.336.0
|
|
73
|
+
with:
|
|
74
|
+
ruby-version: '3.3'
|
|
75
|
+
- name: Check the version
|
|
76
|
+
id: check
|
|
77
|
+
shell: bash -eo pipefail {0}
|
|
78
|
+
env:
|
|
79
|
+
INPUT_VERSION: ${{ inputs.version }}
|
|
80
|
+
DRY_RUN: ${{ inputs.dry_run }}
|
|
81
|
+
run: |
|
|
82
|
+
if [ "$GITHUB_EVENT_NAME" = workflow_dispatch ]; then
|
|
83
|
+
version="$INPUT_VERSION"
|
|
84
|
+
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "::error::the version input is not a plain version like 1.2.3"; exit 1; }
|
|
85
|
+
case "$GITHUB_REF" in
|
|
86
|
+
"refs/heads/$TRUNK"|"refs/heads/${version%.*}-stable") ;;
|
|
87
|
+
*) echo "::error::run this on $TRUNK or ${version%.*}-stable, not $GITHUB_REF"; exit 1 ;;
|
|
88
|
+
esac
|
|
89
|
+
tag="$TAG_PREFIX$version"
|
|
90
|
+
tagged=$(git ls-remote origin "refs/tags/$tag*" | awk -v t="refs/tags/$tag" '$2 == t || $2 == t "^{}" { print $1 }')
|
|
91
|
+
[ -z "$tagged" ] || echo "$tagged" | grep -qx "$GITHUB_SHA" ||
|
|
92
|
+
{ echo "::error::tag $tag already exists at another commit"; exit 1; }
|
|
93
|
+
else
|
|
94
|
+
tag="$GITHUB_REF_NAME"
|
|
95
|
+
version="${tag#"$TAG_PREFIX"}"
|
|
96
|
+
[[ "$tag" == "$TAG_PREFIX"* && "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "::error::tag $tag is not ${TAG_PREFIX}X.Y.Z"; exit 1; }
|
|
97
|
+
fi
|
|
98
|
+
file_version=$(ruby -e 'puts File.binread(ENV["VERSION_FILE"])[/\bVERSION\s*=\s*["\x27]([^"\x27]+)/, 1]')
|
|
99
|
+
[ "$version" = "$file_version" ] || { echo "::error::version $version but $VERSION_FILE says $file_version"; exit 1; }
|
|
100
|
+
if [ "$DRY_RUN" = true ]; then
|
|
101
|
+
echo "::warning::dry run: not checking $CHANGELOG"
|
|
102
|
+
else
|
|
103
|
+
heading=$(grep -m1 -E "$CHANGELOG_HEADING" "$CHANGELOG") || { echo "::error::$CHANGELOG has no heading matching '$CHANGELOG_HEADING'"; exit 1; }
|
|
104
|
+
top=$(echo "$heading" | sed -nE "s/$CHANGELOG_TOP.*/\1/p")
|
|
105
|
+
[ "$top" = "$version" ] || { echo "::error::$CHANGELOG starts with '$heading', not release $version: merge the release PR first, and nothing after it"; exit 1; }
|
|
106
|
+
rest="${heading#*"$version"}"
|
|
107
|
+
[[ "$rest" =~ (19|20)[0-9][0-9] ]] && ! echo "$rest" | grep -qiwE 'unreleased|tbd|tba' ||
|
|
108
|
+
{ echo "::error::$CHANGELOG's '$heading' isn't stamped with a date yet"; exit 1; }
|
|
109
|
+
fi
|
|
110
|
+
series="${version%.*}-stable"
|
|
111
|
+
on=""
|
|
112
|
+
for b in "$series" "$TRUNK"; do
|
|
113
|
+
git merge-base --is-ancestor "$GITHUB_SHA" "refs/remotes/origin/$b" 2>/dev/null && on="$b"
|
|
114
|
+
done
|
|
115
|
+
[ -n "$on" ] || { echo "::error::$version ($GITHUB_SHA) is not on $TRUNK or $series"; exit 1; }
|
|
116
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
117
|
+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
|
118
|
+
echo "$version ($GITHUB_SHA) is on $on"
|
|
119
|
+
- name: Build the gems
|
|
120
|
+
shell: bash -eo pipefail {0}
|
|
121
|
+
env:
|
|
122
|
+
VERSION: ${{ steps.check.outputs.version }}
|
|
123
|
+
run: |
|
|
124
|
+
SOURCE_DATE_EPOCH=$(git log -1 --format=%ct "$GITHUB_SHA"); export SOURCE_DATE_EPOCH
|
|
125
|
+
mkdir dist
|
|
126
|
+
for spec in $GEMS; do
|
|
127
|
+
g="${spec%%:*}"; dir="${spec#*:}"; [ "$dir" = "$spec" ] && dir=.
|
|
128
|
+
out="$GITHUB_WORKSPACE/dist/$g-$VERSION.gem"
|
|
129
|
+
(cd "$dir" && gem build "$g.gemspec" --output "$out")
|
|
130
|
+
[ "$(gem spec "$out" name)" = "--- $g" ] && [ "$(gem spec "$out" version | sed -n 's/^version: //p')" = "$VERSION" ] ||
|
|
131
|
+
{ echo "::error::$out is not $g $VERSION:"; gem spec "$out" name; gem spec "$out" version; exit 1; }
|
|
132
|
+
echo "$g" >> dist/ORDER
|
|
133
|
+
done
|
|
134
|
+
echo "SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH (ruby $(ruby -e 'print RUBY_VERSION'), rubygems $(gem -v))"
|
|
135
|
+
(cd dist && sha256sum *.gem > SHA256SUMS && cat ORDER SHA256SUMS)
|
|
136
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
137
|
+
with:
|
|
138
|
+
name: gems-${{ steps.check.outputs.version }}
|
|
139
|
+
path: dist/
|
|
140
|
+
if-no-files-found: error
|
|
141
|
+
|
|
142
|
+
publish:
|
|
143
|
+
needs: build
|
|
144
|
+
runs-on: ubuntu-latest
|
|
145
|
+
environment: release
|
|
146
|
+
permissions:
|
|
147
|
+
contents: write # creates the tag on a dispatched release
|
|
148
|
+
id-token: write # the RubyGems trusted-publishing exchange
|
|
149
|
+
steps:
|
|
150
|
+
- name: Refuse a stale run (the tag is somewhere else)
|
|
151
|
+
shell: bash -eo pipefail {0}
|
|
152
|
+
env:
|
|
153
|
+
TAG: ${{ needs.build.outputs.tag }}
|
|
154
|
+
run: |
|
|
155
|
+
tagged=$(git ls-remote "https://github.com/$GITHUB_REPOSITORY" "refs/tags/$TAG*" |
|
|
156
|
+
awk -v t="refs/tags/$TAG" '$2 == t || $2 == t "^{}" { print $1 }')
|
|
157
|
+
if [ -z "$tagged" ] && [ "$GITHUB_EVENT_NAME" = workflow_dispatch ]; then
|
|
158
|
+
echo "tag $TAG will be created at $GITHUB_SHA"
|
|
159
|
+
else
|
|
160
|
+
echo "$tagged" | grep -qx "$GITHUB_SHA" || { echo "::error::tag $TAG is not at $GITHUB_SHA (now: ${tagged:-gone}); this run is stale"; exit 1; }
|
|
161
|
+
fi
|
|
162
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
163
|
+
with:
|
|
164
|
+
name: gems-${{ needs.build.outputs.version }}
|
|
165
|
+
path: dist
|
|
166
|
+
- name: Check the sums
|
|
167
|
+
working-directory: dist
|
|
168
|
+
run: sha256sum -c SHA256SUMS
|
|
169
|
+
- uses: ruby/setup-ruby@de492cf36cbab201753d720ce4f4e336cd688c94 # v1.336.0
|
|
170
|
+
with:
|
|
171
|
+
ruby-version: '3.3'
|
|
172
|
+
- uses: rubygems/configure-rubygems-credentials@dc5a8d8553e6ee01fc26761a49e99e733d17954a # v2.1.0
|
|
173
|
+
- name: Dry run ends here
|
|
174
|
+
if: inputs.dry_run
|
|
175
|
+
run: echo "Credentials exchange succeeded, so the trusted publisher works. No tag created, nothing pushed."
|
|
176
|
+
- name: Push the gems
|
|
177
|
+
if: ${{ !inputs.dry_run }}
|
|
178
|
+
shell: bash -eo pipefail {0}
|
|
179
|
+
working-directory: dist
|
|
180
|
+
env:
|
|
181
|
+
VERSION: ${{ needs.build.outputs.version }}
|
|
182
|
+
run: |
|
|
183
|
+
while read -r g; do
|
|
184
|
+
sum=$(sha256sum "$g-$VERSION.gem" | cut -d' ' -f1)
|
|
185
|
+
published=$(curl -fsS "https://rubygems.org/api/v1/versions/$g.json" 2>/dev/null |
|
|
186
|
+
ruby -rjson -e 'b = $stdin.read; v = (b.empty? ? [] : JSON.parse(b)).find { |x| x["number"] == ENV["VERSION"] && x["platform"] == "ruby" }; print v ? v["sha"].to_s : ""' || true)
|
|
187
|
+
if [ -z "$published" ]; then
|
|
188
|
+
gem push "$g-$VERSION.gem" </dev/null
|
|
189
|
+
elif [ "$published" = "$sum" ]; then
|
|
190
|
+
echo "$g $VERSION is already on rubygems.org with this sum; skipping"
|
|
191
|
+
else
|
|
192
|
+
echo "::error::$g $VERSION is already on rubygems.org with sha256 $published, not this build's $sum"; exit 1
|
|
193
|
+
fi
|
|
194
|
+
done < ORDER
|
|
195
|
+
- name: Create the tag
|
|
196
|
+
if: github.event_name == 'workflow_dispatch' && !inputs.dry_run
|
|
197
|
+
shell: bash -eo pipefail {0}
|
|
198
|
+
env:
|
|
199
|
+
TAG: ${{ needs.build.outputs.tag }}
|
|
200
|
+
GH_TOKEN: ${{ github.token }}
|
|
201
|
+
run: |
|
|
202
|
+
tagged=$(git ls-remote "https://github.com/$GITHUB_REPOSITORY" "refs/tags/$TAG*" |
|
|
203
|
+
awk -v t="refs/tags/$TAG" '$2 == t || $2 == t "^{}" { print $1 }')
|
|
204
|
+
if [ -z "$tagged" ]; then
|
|
205
|
+
gh api "repos/$GITHUB_REPOSITORY/git/refs" -f ref="refs/tags/$TAG" -f sha="$GITHUB_SHA"
|
|
206
|
+
elif echo "$tagged" | grep -qx "$GITHUB_SHA"; then
|
|
207
|
+
echo "tag $TAG is already at $GITHUB_SHA"
|
|
208
|
+
else
|
|
209
|
+
echo "::error::tag $TAG is not at $GITHUB_SHA (now: $tagged)"; exit 1
|
|
210
|
+
fi
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 (2026-10-31)
|
|
4
|
+
|
|
5
|
+
Changes since 0.2.0:
|
|
6
|
+
|
|
7
|
+
- **Ruby 2.7 or newer is now required** (the gemspec declares it). Stay on 0.2.0 for older Rubies. (#21, @nesquena)
|
|
8
|
+
- **New `-f`/`--force` option** (`:force => true`): when a stop's SIGTERM does not stop the daemon within 5 seconds,
|
|
9
|
+
dante sends SIGKILL. (#13, @hayesgm)
|
|
10
|
+
- **Changed: stopping keeps the pid file until the daemon has stopped.** `-k`/`stop` used to delete the pid file
|
|
11
|
+
before signalling; it now deletes it only once the daemon has exited, and keeps it (and says "Failed to kill
|
|
12
|
+
daemonized process") when the daemon survives. The log line is now "Sent SIGTERM to PID: ..." instead of
|
|
13
|
+
"Stopped PID: ...": update anything that matches the old text. (#13, @hayesgm)
|
|
14
|
+
- **Changed: user and group switching as root** (`-u`, `-G`, `:user`, `:group`). Everything is looked up before
|
|
15
|
+
anything changes. `-u` now takes the user's primary group (or the `-G` group) and the user's own supplementary
|
|
16
|
+
groups; before, the daemon kept gid 0 and root's supplementary groups. `-G` alone leaves only that group. If your
|
|
17
|
+
daemon relied on root's groups, add the user to the groups it needs or pass `-G`. Where root may not change its
|
|
18
|
+
groups (a restricted capability set, or a user namespace that denies setgroups), `-u` now fails at startup with
|
|
19
|
+
`Errno::EPERM`, as `su` does. A non-root process changes only what it was given, as before. (#22, @nesquena)
|
|
20
|
+
- **Changed: the daemon keeps the caller's umask** instead of setting it to 0000, so new files are 644 and
|
|
21
|
+
directories 755 under umask 022 (they were 666 and 777). If the daemon must create world-writable files, set
|
|
22
|
+
`File.umask` in the startup block. The log file stays 0644. (#22, @nesquena)
|
|
23
|
+
- **Changed: a daemon now counts as started only if it is still running 1 second after it was forked**, so
|
|
24
|
+
`-d` takes up to a second longer. A daemon that exits at once (its startup block raised, or returned) is now
|
|
25
|
+
reported as "Daemonized process couldn't be started", `daemonize` returns false, and the pid file it left is
|
|
26
|
+
removed; before, it could be reported as started, and its stale pid stayed in the pid file. The exit status of
|
|
27
|
+
your executable doesn't change (dante doesn't set it): to act on a failed start, check what `execute` returns.
|
|
28
|
+
(#24, @nesquena)
|
|
29
|
+
- **Changed: the process between the caller and the daemon no longer runs the program's `at_exit` handlers**; it
|
|
30
|
+
leaves with `exit!`, as Ruby's `Process.daemon` does. They used to run in the two intermediate processes right
|
|
31
|
+
after the fork, so a cleanup handler (stripe-ruby-mock's `kill_server`, say) ran there too. Handlers still run in
|
|
32
|
+
the caller and in the daemon; nothing to do unless you relied on that extra run. (#24, @nesquena)
|
|
33
|
+
- Fixed: output redirection when the standard streams are sockets (Spring, Zeus): output now goes to the log
|
|
34
|
+
instead of raising `can't change access mode`. Reported by @antonfefilov in #20. (#22, @nesquena)
|
|
35
|
+
- Fixed: a pid file naming another user's process no longer crashes with `Errno::EPERM`: starting prints "Process
|
|
36
|
+
is already started", and `-k` reports that it could not stop the process and keeps the pid file. (#22, @nesquena)
|
|
37
|
+
- Fixed: `-k` on another user's process gives up at once (in about 0.2 seconds) instead of waiting 5 seconds, or
|
|
38
|
+
10 with `--force`, for a process it cannot signal. (#24, @nesquena)
|
|
39
|
+
- Fixed: an empty pid file (left by a crash between creating and writing it), or one holding 0 or a negative
|
|
40
|
+
number, counts as no daemon. Before, starting refused with "Process is already started", and `-k` sent SIGTERM
|
|
41
|
+
to its own process group, killing itself and the processes started alongside it. A number too large for a pid
|
|
42
|
+
counts as no daemon too, where it raised `RangeError`. (#24, @nesquena)
|
|
43
|
+
- Fixed: daemonizing from a program that ignores SIGCHLD (`trap('CHLD', 'IGNORE')`) raised `Errno::ECHILD`; it now
|
|
44
|
+
reports whether the daemon started. (#24, @nesquena)
|
|
45
|
+
- Fixed: INT and TERM are handled before the pid file is written, so a `-k` right after start goes through
|
|
46
|
+
dante's handler. (#22, @nesquena)
|
|
47
|
+
- The gemspec now declares the MIT license and points its homepage at github.com/nesquena/dante, and the
|
|
48
|
+
deprecated `rubyforge_project` setting is gone. (#27, @nesquena)
|
|
49
|
+
- Development: the test suite is reliable on current Ruby and minitest (mocha is no longer needed), `rake` runs
|
|
50
|
+
it, and GitHub Actions runs it on Ruby 2.7 to 4.0. Reported by @balasankarc in #19. (#21, #23, @nesquena)
|
|
51
|
+
|
|
52
|
+
## 0.2.0 (2013-12-04)
|
|
53
|
+
|
|
54
|
+
- A `verify_options_hook` to validate options; daemon mode logs to `/var/log/<name>.log` by default; a log file
|
|
55
|
+
given in the foreground is used. (#7, #9, #10, @rkatti)
|
|
56
|
+
- The daemonizing process reaps its child, leaving no zombie. (#5, @jonigata)
|
|
57
|
+
|
|
58
|
+
## 0.1.5 (2012-06-21)
|
|
59
|
+
|
|
60
|
+
- Fixes for `kill_pid`, `interrupt` and `log`. (@mckern)
|
|
61
|
+
|
|
62
|
+
## 0.1.4 (2012-06-20)
|
|
63
|
+
|
|
64
|
+
- Fixed user and group switching and log redirection; cleaner shutdown message. (#1, @mckern)
|
|
65
|
+
|
|
66
|
+
## 0.1.3 (2012-01-30)
|
|
67
|
+
|
|
68
|
+
- `-l`/`--log`: a log file for the daemon's output.
|
|
69
|
+
|
|
70
|
+
## 0.1.2 (2011-12-06)
|
|
71
|
+
|
|
72
|
+
- `daemon_running?` and `daemon_stopped?` are public.
|
|
73
|
+
|
|
74
|
+
## 0.1.1 (2011-12-06)
|
|
75
|
+
|
|
76
|
+
- Better handling of the debug option.
|
|
77
|
+
|
|
78
|
+
## 0.1.0 (2011-12-06)
|
|
79
|
+
|
|
80
|
+
- A more robust runner: stops and starts are checked, and `restart` is added.
|
|
81
|
+
|
|
82
|
+
## 0.0.4 (2011-11-30)
|
|
83
|
+
|
|
84
|
+
- Stopping sends INT instead of KILL. (@joshbuddy)
|
|
85
|
+
|
|
86
|
+
## 0.0.3 (2011-11-30)
|
|
87
|
+
|
|
88
|
+
- Cleaner process stopping.
|
|
89
|
+
|
|
90
|
+
## 0.0.2 (2011-11-28)
|
|
91
|
+
|
|
92
|
+
- Custom options (`with_options`), a custom banner, and options passed to the startup block.
|
|
93
|
+
|
|
94
|
+
## 0.0.1 (2011-11-21)
|
|
95
|
+
|
|
96
|
+
- First release.
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2011
|
|
1
|
+
Copyright (c) 2011-2026 Nathan Esquenazi
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
4
|
a copy of this software and associated documentation files (the
|
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -65,12 +65,28 @@ This gives your binary several useful things for free:
|
|
|
65
65
|
|
|
66
66
|
will start the app undaemonized in the terminal, handling trapping and stopping the process.
|
|
67
67
|
|
|
68
|
+
```
|
|
69
|
+
./bin/myapp -l /var/log/myapp.log
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
will start the app undaemonized in the terminal and redirect all stdout and stderr to the specified logfile.
|
|
73
|
+
|
|
68
74
|
```
|
|
69
75
|
./bin/myapp -p 8080 -d -P /var/run/myapp.pid -l /var/log/myapp.log
|
|
70
76
|
```
|
|
71
77
|
|
|
72
|
-
will daemonize and start the process, storing the pid in the specified pid file.
|
|
73
|
-
|
|
78
|
+
will daemonize and start the process, storing the pid in the specified pid file. It reports the daemon as
|
|
79
|
+
started once the daemon has kept running for a second, and as not started if it exits before then.
|
|
80
|
+
All stdout and stderr will be redirected to the specified logfile. If no logfile is specified in daemon mode then all
|
|
81
|
+
stdout and stderr will be directed to /var/log/<myapp name>.log.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
./bin/myapp -d -u nobody -G nogroup
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
will switch to the user nobody before starting, with the group given by `-G`. When started as root, the group
|
|
88
|
+
defaults to the user's primary group, and the process takes on the user's supplementary groups (plus the `-G`
|
|
89
|
+
group, if given).
|
|
74
90
|
|
|
75
91
|
```
|
|
76
92
|
./bin/myapp -k -P /var/run/myapp.pid
|
|
@@ -104,6 +120,10 @@ runner.with_options do |opts|
|
|
|
104
120
|
options[:test] = test
|
|
105
121
|
end
|
|
106
122
|
end
|
|
123
|
+
# Create validation hook for options
|
|
124
|
+
runner.verify_options_hook = lambda { |opts|
|
|
125
|
+
raise Exception.new("Must supply test parameter") if opts[:test].nil?
|
|
126
|
+
}
|
|
107
127
|
# Parse command-line options and execute the process
|
|
108
128
|
runner.execute do |opts|
|
|
109
129
|
# opts: host, pid_path, port, daemonize, user, group
|
|
@@ -170,4 +190,4 @@ and that's all. Of course now you can also easily daemonize as well as start/sto
|
|
|
170
190
|
|
|
171
191
|
## Copyright
|
|
172
192
|
|
|
173
|
-
Copyright © 2011 Nathan Esquenazi. See [LICENSE](https://github.com/
|
|
193
|
+
Copyright © 2011-2026 Nathan Esquenazi. See [LICENSE](https://github.com/nesquena/dante/blob/master/LICENSE) for details.
|
data/Rakefile
CHANGED
|
@@ -2,14 +2,22 @@ require 'bundler/gem_tasks'
|
|
|
2
2
|
require 'rake/testtask'
|
|
3
3
|
# require 'yard'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
TEST_FILES = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)]
|
|
6
|
+
|
|
7
|
+
Rake::TestTask.new do |t|
|
|
8
|
+
t.libs.push "lib"
|
|
9
|
+
t.test_files = TEST_FILES
|
|
10
|
+
t.verbose = true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Rake::TestTask treats an empty file list as a passing run of nothing.
|
|
14
|
+
task :test_files_present do
|
|
15
|
+
abort "No test files match test/**/*_test.rb" if TEST_FILES.empty?
|
|
11
16
|
end
|
|
17
|
+
task :test => :test_files_present
|
|
18
|
+
|
|
19
|
+
task :default => :test
|
|
12
20
|
|
|
13
21
|
# task :doc do
|
|
14
22
|
# YARD::CLI::Yardoc.new.run
|
|
15
|
-
# end
|
|
23
|
+
# end
|
data/dante.gemspec
CHANGED
|
@@ -7,18 +7,17 @@ Gem::Specification.new do |s|
|
|
|
7
7
|
s.version = Dante::VERSION
|
|
8
8
|
s.authors = ["Nathan Esquenazi"]
|
|
9
9
|
s.email = ["nesquena@gmail.com"]
|
|
10
|
-
s.homepage = "https://github.com/
|
|
10
|
+
s.homepage = "https://github.com/nesquena/dante"
|
|
11
11
|
s.summary = %q{Turn any process into a demon}
|
|
12
12
|
s.description = %q{Turn any process into a demon.}
|
|
13
|
-
|
|
14
|
-
s.rubyforge_project = "dante"
|
|
13
|
+
s.license = "MIT"
|
|
15
14
|
|
|
16
15
|
s.files = `git ls-files`.split("\n")
|
|
17
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
18
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
19
18
|
s.require_paths = ["lib"]
|
|
19
|
+
s.required_ruby_version = '>= 2.7'
|
|
20
20
|
|
|
21
21
|
s.add_development_dependency 'rake'
|
|
22
|
-
s.add_development_dependency 'minitest'
|
|
23
|
-
s.add_development_dependency 'mocha'
|
|
22
|
+
s.add_development_dependency 'minitest', '>= 5.0'
|
|
24
23
|
end
|