1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// Package release runs an app's own release-phase Makefile target, if it
// declares one - mirroring Heroku's Procfile `release:` line, but as a
// `release` target in the same Makefile that builds the app. Skiff never
// interprets what the target does (run migrations, warm a cache,
// whatever); it only checks whether one is declared and gates the deploy
// on its exit code.
package release
import (
"context"
"fmt"
"io"
"strings"
"skiff/internal/sh"
)
// notDeclared is bmake's exact wording (verified against FreeBSD 15's
// /usr/bin/make) when asked to build a target with no matching rule -
// explicit, implicit, or file-on-disk. Skiff is FreeBSD-only, so this
// doesn't need to also cover GNU make's differently-worded message.
const notDeclared = "don't know how to make release"
// Declared reports whether dir's Makefile defines a `release` target, by
// asking bmake itself via a dry run rather than parsing Makefile syntax -
// robust regardless of how the target is actually defined (directly, via
// an include, a conditional, a pattern rule). dir is the release's
// host-side checkout; this runs on the host, before the jail is involved.
func Declared(ctx context.Context, dir string) (bool, error) {
out, err := sh.CombinedOutput(ctx, dir, "make", "-n", "release")
if err == nil {
return true, nil
}
if strings.Contains(out, notDeclared) {
return false, nil
}
return false, fmt.Errorf("make -n release: %w\n%s", err, out)
}
// Run executes the `release` Makefile target as a one-off inside app's
// already-running jail (the jail holds the process's filesystem and any
// state the target needs to reach - e.g. the app's own data.db).
// releaseDirInJail must be the new release's own checkout, jail-relative -
// not `current`, which still points at the previous release until cutover
// happens after this returns successfully. -B forces the recipe to run
// even if a stray file/directory named `release` already exists in the
// checkout. A non-nil error means the deploy must abort before
// restart/cutover.
func Run(ctx context.Context, out io.Writer, app, releaseDirInJail string) error {
fmt.Fprintf(out, "==> release: make release (in %s)\n", releaseDirInJail)
return sh.Run(ctx, out, "", "jexec", app, "make", "-B", "-C", releaseDirInJail, "release")
}
|