Skip to content

Commit 1692772

Browse files
ipmbclaude
andcommitted
Warn on EOL stacks (heroku-18, heroku-20) and add builder tests
Closes #25 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c1cf649 commit 1692772

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

builder/build/appjson.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ func patchBuildpack(buildpack string, stack string) string {
101101
return buildpack
102102
}
103103

104+
// EOLStacks contains stacks that have reached end-of-life
105+
var EOLStacks = []string{"heroku-18", "heroku-20"}
106+
104107
func (a *AppJSON) Unmarshal() error {
105108
content, err := a.reader()
106109
if err != nil {
@@ -115,6 +118,9 @@ func (a *AppJSON) Unmarshal() error {
115118
log.Ctx(a.ctx).Error().Err(err).Msg("failed to parse app.json")
116119
return err
117120
}
121+
if contains(EOLStacks, a.Stack) {
122+
log.Ctx(a.ctx).Warn().Str("stack", a.Stack).Msg("stack is end-of-life and no longer supported; upgrade to heroku-24")
123+
}
118124
return nil
119125
}
120126

builder/build/appjson_test.go

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"os"
66
"reflect"
7+
"strings"
78
"testing"
89

910
"github.com/rs/zerolog"
@@ -72,13 +73,40 @@ func TestAppJsonStack(t *testing.T) {
7273
}
7374

7475
func TestAppJsonBuilders(t *testing.T) {
75-
a := AppJSON{
76-
Stack: "heroku-22",
77-
ctx: testContext,
76+
tests := []struct {
77+
stack string
78+
expected []string
79+
}{
80+
{"heroku-18", []string{"heroku/buildpacks:18", "heroku/heroku:18-cnb"}},
81+
{"heroku-20", []string{"heroku/buildpacks:20", "heroku/heroku:20-cnb"}},
82+
{"heroku-22", []string{"heroku/builder:22", "heroku/heroku:22-cnb"}},
83+
{"custom/builder:latest", []string{"custom/builder:latest"}},
84+
}
85+
for _, tt := range tests {
86+
a := AppJSON{Stack: tt.stack, ctx: testContext}
87+
if !stringSliceEqual(a.GetBuilders(), tt.expected) {
88+
t.Errorf("stack %s: expected %s, got %s", tt.stack, tt.expected, a.GetBuilders())
89+
}
7890
}
79-
expected := []string{"heroku/builder:22", "heroku/heroku:22-cnb"}
80-
if !stringSliceEqual(a.GetBuilders(), expected) {
81-
t.Errorf("expected %s, got %s", expected, a.GetBuilders())
91+
}
92+
93+
func TestAppJsonEOLStackWarning(t *testing.T) {
94+
for _, stack := range EOLStacks {
95+
var buf strings.Builder
96+
logger := zerolog.New(&buf)
97+
ctx := logger.WithContext(context.Background())
98+
a := AppJSON{
99+
reader: func() ([]byte, error) {
100+
return []byte(`{"stack": "` + stack + `"}`), nil
101+
},
102+
ctx: ctx,
103+
}
104+
if err := a.Unmarshal(); err != nil {
105+
t.Fatalf("stack %s: unexpected error: %s", stack, err)
106+
}
107+
if !strings.Contains(buf.String(), "end-of-life") {
108+
t.Errorf("stack %s: expected EOL warning in log output, got: %s", stack, buf.String())
109+
}
82110
}
83111
}
84112

0 commit comments

Comments
 (0)