From a43051c5208e1eb2e6b77592c3ae4fa2c2c9b76a Mon Sep 17 00:00:00 2001 From: Christian Gregg Date: Wed, 1 Dec 2021 23:16:36 +0000 Subject: [PATCH] Remove docker commands (#75) Simplifies things down to their core. --- .github/workflows/acceptance.yml | 8 --- .github/workflows/build.yml | 8 --- .github/workflows/release.yml | 26 -------- Dockerfile | 19 ------ README.md | 25 ------- cmd/slugcmplr/compile.go | 38 ++--------- cmd/slugcmplr/image.go | 111 ------------------------------- cmd/slugcmplr/main.go | 1 - cmd/slugcmplr/main_test.go | 78 +--------------------- compile_docker.go | 38 ----------- image.go | 93 -------------------------- shared.go | 28 -------- 12 files changed, 6 insertions(+), 467 deletions(-) delete mode 100644 Dockerfile delete mode 100644 cmd/slugcmplr/image.go delete mode 100644 compile_docker.go delete mode 100644 image.go diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index a7393ba7..4441cca8 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -24,13 +24,5 @@ jobs: - uses: actions/setup-go@v2 with: go-version: 1.17.x - - uses: docker/setup-qemu-action@v1 - - uses: docker/setup-buildx-action@v1 - - uses: docker/build-push-action@v2 - with: - context: . - tags: ghcr.io/cga1123/slugcmplr:testing - load: true - build-args: STACK=20 - run: go get -v -d ./... - run: go test -v -race -parallel=4 ./... diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 516e632e..b6289727 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,14 +29,6 @@ jobs: - uses: actions/setup-go@v2 with: go-version: 1.17.x - - uses: docker/setup-qemu-action@v1 - - uses: docker/setup-buildx-action@v1 - - uses: docker/build-push-action@v2 - with: - context: . - tags: ghcr.io/cga1123/slugcmplr:testing - load: true - build-args: STACK=20 - run: go get -v -d ./... - run: bin/go-post-compile - run: script/migrate up diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8882e72c..dc987862 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,32 +5,6 @@ on: - '*' jobs: - containers: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - stack: [ '18', '20' ] - - steps: - - uses: actions/checkout@v2.4.0 - - uses: docker/setup-qemu-action@v1 - - uses: docker/setup-buildx-action@v1 - - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: docker build - run: | - docker build \ - --build-arg STACK=${{ matrix.stack }} \ - --tag ghcr.io/cga1123/slugcmplr:heroku-${{ matrix.stack }} \ - --tag ghcr.io/cga1123/slugcmplr:heroku-${{ matrix.stack }}-${{ github.sha }} \ - . - - run: docker push ghcr.io/cga1123/slugcmplr:heroku-${{ matrix.stack }} - - run: docker push ghcr.io/cga1123/slugcmplr:heroku-${{ matrix.stack }}-${{ github.sha }} - goreleaser: runs-on: ubuntu-latest steps: diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 0ded825d..00000000 --- a/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -ARG STACK - -FROM golang:1.17-buster AS builder - -COPY . /app -WORKDIR /app - -RUN go mod download - -# TODO: use goreleaser? or pass in version, commit, and date as build-args? -RUN CGO_ENABLED=0 go build -o bin/ ./cmd/slugcmplr - -FROM heroku/heroku:${STACK}-build - -LABEL org.opencontainers.image.source="https://github.com/CGA1123/slugcmplr" - -COPY --from=builder /app/bin/slugcmplr /usr/bin/slugcmplr - -ENTRYPOINT ["/usr/bin/slugcmplr"] diff --git a/README.md b/README.md index c3cf2876..2f486c3e 100644 --- a/README.md +++ b/README.md @@ -65,15 +65,6 @@ uploaded to Heroku. The `CACHE-DIR` will be used by the buildpacks as their cache argument to speed up builds in the future, as per the [Buildpack API](https://devcenter.heroku.com/articles/buildpack-api) -You can optionally pass `--local` to run the compile steps locally, outside of -any docker image. - -You can optionally pass `--image [IMAGE]` to use a custom docker image, -`slugcmplr` assumes that a version of itself is available at `bin/slugcmplr` in -the image. You can use the `%stack%` pattern in `IMAGE` to have the stack (e.g. -`heroku-20`) a part of the image name. - - #### `release --build-dir [BUILD-DIR]` In the release step, `slugcmplr` triggers a release of your previously compiled @@ -87,22 +78,6 @@ different from the one you build from. This will work as long as the applications are in the same Heroku team. (This is becuase the slug must be accessible to the application). -#### `image --build-dir [BUILD-DIR] --cmd [CMD]` - -The `image` subcommand builds a container image using `docker build`. By -default it uses a Heroku Stack Image (e.g. `heroku/heroku:20`) that is -compatible with your application. - -You can optionally pass `--image [IMAGE]` to use a custom base image. You can -use the `%stack%` pattern in `IMAGE` to have a stack _number_ (e.g. `20`) as -part of the image name or tag. - -The image is build by copying the contents of your application after building -into `/app`. - -The `CMD` is the command that Heroku will use in its container runtime to start -your application. (e.g. `bundle exec puma -p $PORT`, `bin/server --port $PORT`). - ## Authentication The `slugcmplr` CLI looks for credentials to `api.heroku.com` in your `.netrc` diff --git a/cmd/slugcmplr/compile.go b/cmd/slugcmplr/compile.go index e2d41605..8df07865 100644 --- a/cmd/slugcmplr/compile.go +++ b/cmd/slugcmplr/compile.go @@ -14,8 +14,6 @@ import ( "github.com/spf13/cobra" ) -const defaultImage = "ghcr.io/cga1123/slugcmplr:" + slugcmplr.StackReplacePattern - // Compile contains the configuration required for the compile subcommand. type Compile struct { Application string `json:"application"` @@ -82,8 +80,7 @@ func compile(ctx context.Context, out outputter, h *heroku.Service, c *Compile, } func compileCmd(verbose bool) *cobra.Command { - var cacheDir, buildDir, image string - var local bool + var cacheDir, buildDir string cmd := &cobra.Command{ Use: "compile", @@ -118,46 +115,19 @@ func compileCmd(verbose bool) *cobra.Command { return fmt.Errorf("failed to decode metadata: %w", err) } - if local { - client, err := netrcClient(output) - if err != nil { - return err - } - - return compile(cmd.Context(), output, client, c, buildDir, cacheDir) - } - - netrcpath, err := netrcPath() + client, err := netrcClient(output) if err != nil { - return fmt.Errorf("failed to find netrc path: %w", err) - } - - compileDocker := &slugcmplr.CompileDockerCmd{ - BuildDir: buildDir, - CacheDir: cacheDir, - NetrcPath: netrcpath, - Image: image, - Stack: c.Stack, + return err } - return compileDocker.Execute(cmd.Context(), output) + return compile(cmd.Context(), output, client, c, buildDir, cacheDir) }, } cmd.Flags().StringVar(&buildDir, "build-dir", "", "The build directory") cmd.MarkFlagRequired("build-dir") // nolint:errcheck - cmd.Flags().BoolVar(&local, "local", false, "Run compilation locally") cmd.Flags().StringVar(&cacheDir, "cache-dir", "", "The cache directory") - cmd.Flags().StringVar( - &image, - "image", - defaultImage, - fmt.Sprintf( - "Override docker image to use, include %s in order to substitute the stack name", - slugcmplr.StackReplacePattern, - ), - ) return cmd } diff --git a/cmd/slugcmplr/image.go b/cmd/slugcmplr/image.go deleted file mode 100644 index 93248ec3..00000000 --- a/cmd/slugcmplr/image.go +++ /dev/null @@ -1,111 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/cga1123/slugcmplr" - "github.com/cga1123/slugcmplr/buildpack" - "github.com/cga1123/slugcmplr/processfile" - "github.com/spf13/cobra" -) - -func imageCmd(verbose bool) *cobra.Command { - var buildDir, img, command, process string - var noBuild bool - - cmd := &cobra.Command{ - Use: "image", - Short: "build a container from your compiled application", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - output := outputterFromCmd(cmd, verbose) - - dbg(output, "buildDir: %v", buildDir) - dbg(output, "image: %v", img) - dbg(output, "command: %v", command) - dbg(output, "process: %v", process) - - if process == "" && command == "" { - return fmt.Errorf("either --cmd or --process must be provided") - } - - if process != "" { - c, err := commandFromProcfile(buildDir, process) - if err != nil { - return fmt.Errorf("error determining command from Procfile: %w", err) - } - - command = c - } - - m, err := os.Open(filepath.Join(buildDir, "meta.json")) - if err != nil { - return fmt.Errorf("failed to read metadata: %w", err) - } - defer m.Close() // nolint:errcheck - - c := &Compile{} - if err := json.NewDecoder(m).Decode(c); err != nil { - return fmt.Errorf("failed to decode metadata: %w", err) - } - - i := &slugcmplr.ImageCmd{ - BuildDir: buildDir, - Image: img, - Command: command, - Stack: c.Stack, - NoBuild: noBuild, - } - - return i.Execute(cmd.Context(), output) - }, - } - - cmd.Flags().StringVar(&buildDir, "build-dir", "", "The build directory") - cmd.MarkFlagRequired("build-dir") // nolint:errcheck - - cmd.Flags().StringVar(&command, "cmd", "", "The command (CMD) to run by default") - cmd.Flags().StringVar( - &process, - "process", - "", - "The command (CMD) to run by default, based on Procfile entries. Takes precedence over --cmd", - ) - - cmd.Flags().BoolVar(&noBuild, "no-build", false, "Skip building the image, only generate the Dockerfile") - cmd.Flags().StringVar( - &img, - "image", - fmt.Sprintf("heroku/heroku:%s", slugcmplr.StackNumberReplacePattern), - fmt.Sprintf( - "Override docker image to use, include %s in order to substitute the stack number", - slugcmplr.StackNumberReplacePattern, - ), - ) - - return cmd -} - -func commandFromProcfile(buildDir, process string) (string, error) { - f, err := os.Open(filepath.Join(buildDir, buildpack.AppDir, "Procfile")) - if err != nil { - return "", fmt.Errorf("error opening Procfile: %w", err) - } - defer f.Close() // nolint:errcheck - - pf, err := processfile.Read(f) - if err != nil { - return "", fmt.Errorf("error reading Procfile: %w", err) - } - - c, ok := pf.Entrypoint(process) - if !ok { - return "", fmt.Errorf("%v is not defined in Procfile (%v available)", process, strings.Join(pf.Processes(), ", ")) - } - - return c, nil -} diff --git a/cmd/slugcmplr/main.go b/cmd/slugcmplr/main.go index bdd0e071..40ee8964 100644 --- a/cmd/slugcmplr/main.go +++ b/cmd/slugcmplr/main.go @@ -56,7 +56,6 @@ func Cmd() *cobra.Command { prepareCmd, compileCmd, releaseCmd, - imageCmd, versionCmd, serverCmd, workerCmd, diff --git a/cmd/slugcmplr/main_test.go b/cmd/slugcmplr/main_test.go index e1ea2212..d818539e 100644 --- a/cmd/slugcmplr/main_test.go +++ b/cmd/slugcmplr/main_test.go @@ -34,78 +34,6 @@ func Test_Suite(t *testing.T) { }) } -func Test_Image_WithProcess(t *testing.T) { - t.Parallel() - - buildpacks := []*slugcmplr.BuildpackReference{ - {URL: "https://github.com/CGA1123/heroku-buildpack-bar", Name: "CGA1123/heroku-buildpack-bar"}, - } - - configVars := map[string]string{"FOO": "BAR", "BAR": "FOO"} - - withStubPrepare(t, "CGA1123/slugcmplr-fixture-binary", buildpacks, configVars, func(t *testing.T, app, buildDir string) { - imageCmd := Cmd() - imageCmd.SetArgs([]string{ - "image", - "--build-dir", buildDir, - "--image", "test-image:%stack%", - "--process", "web", - "--no-build", - }) - - ok(t, imageCmd.Execute()) - - b, err := os.ReadFile(filepath.Join(buildDir, "Dockerfile")) - ok(t, err) - - dockerfile := string(b) - - if !strings.HasPrefix(dockerfile, "FROM test-image:heroku-20") { - t.Fatalf("expected dockerfile to start with expected base image") - } - - if !strings.HasSuffix(dockerfile, "CMD ./server\n") { - t.Fatalf("expected dockerfile to end with expected CMD from procfile") - } - }) -} - -func Test_Image_WithCmd(t *testing.T) { - t.Parallel() - - buildpacks := []*slugcmplr.BuildpackReference{ - {URL: "https://github.com/CGA1123/heroku-buildpack-bar", Name: "CGA1123/heroku-buildpack-bar"}, - } - - configVars := map[string]string{"FOO": "BAR", "BAR": "FOO"} - - withStubPrepare(t, "CGA1123/slugcmplr-fixture-binary", buildpacks, configVars, func(t *testing.T, app, buildDir string) { - imageCmd := Cmd() - imageCmd.SetArgs([]string{ - "image", - "--build-dir", buildDir, - "--image", "test-image:%stack%", - "--cmd", "bundle exec puma", - "--no-build", - }) - - ok(t, imageCmd.Execute()) - - b, err := os.ReadFile(filepath.Join(buildDir, "Dockerfile")) - ok(t, err) - - dockerfile := string(b) - - if !strings.HasPrefix(dockerfile, "FROM test-image:heroku-20") { - t.Fatalf("expected dockerfile to start with expected base image") - } - - if !strings.HasSuffix(dockerfile, "CMD bundle exec puma\n") { - t.Fatalf("expected dockerfile to end with expected CMD from CLI") - } - }) -} - func testPrepare(t *testing.T) { t.Parallel() @@ -219,8 +147,7 @@ func testDetectFail(t *testing.T) { compileCmd.SetErr(writer) compileCmd.SetArgs([]string{ "compile", - "--build-dir", buildDir, - "--image", "ghcr.io/cga1123/slugcmplr:testing"}) + "--build-dir", buildDir}) compileErr = compileCmd.Execute() @@ -330,8 +257,7 @@ func endToEndSmoke(t *testing.T, fixture string) { compileCmd := Cmd() compileCmd.SetArgs([]string{ "compile", - "--build-dir", buildDir, - "--image", "ghcr.io/cga1123/slugcmplr:testing"}) + "--build-dir", buildDir}) ok(t, compileCmd.Execute()) // Release diff --git a/compile_docker.go b/compile_docker.go deleted file mode 100644 index c9c8410d..00000000 --- a/compile_docker.go +++ /dev/null @@ -1,38 +0,0 @@ -package slugcmplr - -import ( - "context" - "fmt" - "os/exec" -) - -// CompileDockerCmd wraps up all the information required to run compilation -// within a container. -type CompileDockerCmd struct { - BuildDir string - CacheDir string - NetrcPath string - Image string - Stack string -} - -// Execute mounts the build and cache directories, and your netrc file into the -// the container running the provided image, before executing `slugcmplr -// compile`. -func (c *CompileDockerCmd) Execute(ctx context.Context, out Outputter) error { - dockerRun := exec.CommandContext(ctx, "docker", "run", - "--volume", fmt.Sprintf("%v:/tmp/build", c.BuildDir), - "--volume", fmt.Sprintf("%v:/tmp/cache", c.CacheDir), - "--volume", fmt.Sprintf("%v:/tmp/netrc", c.NetrcPath), - "--env", "NETRC=/tmp/netrc", - StackImage(c.Image, c.Stack), - "compile", - "--local", - "--build-dir", "/tmp/build", - "--cache-dir", "/tmp/cache", - ) // #nosec G204 - - dockerRun.Stderr, dockerRun.Stdout = out.ErrOrStderr(), out.OutOrStdout() - - return dockerRun.Run() -} diff --git a/image.go b/image.go deleted file mode 100644 index 54b77683..00000000 --- a/image.go +++ /dev/null @@ -1,93 +0,0 @@ -package slugcmplr - -import ( - "bytes" - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "text/template" - - "github.com/cga1123/slugcmplr/buildpack" -) - -const dockerfileTemplate = `FROM {{.BaseImage}} -RUN groupadd -r dyno && useradd --no-log-init -r -g dyno u1123 -WORKDIR /app -COPY {{.AppDirectory}} /app -RUN chown -R u1123:dyno /app -USER u1123 - -CMD {{.Command}} -` - -type templateVars struct { - BaseImage string - AppDirectory string - Command string -} - -// Dockerfile builds the contents of the dockerfile based on a template. -func Dockerfile(baseImage, appDirectory, command string) ([]byte, error) { - t, err := template.New("").Parse(dockerfileTemplate) - if err != nil { - return nil, fmt.Errorf("failed to build Dockerfile template: %w", err) - } - - b := &bytes.Buffer{} - if err := t.Execute(b, templateVars{ - BaseImage: baseImage, - AppDirectory: appDirectory, - Command: command, - }); err != nil { - return nil, fmt.Errorf("failed to execute Dockerfile template: %w", err) - } - - return b.Bytes(), nil -} - -// ImageCmd wraps up all the information required to build a Docker image from -// a slugcmplr compiled application. -type ImageCmd struct { - BuildDir string - Image string - Stack string - Command string - NoBuild bool -} - -// Execute creates a new Docker image based on a Dockerfile that will be -// written to buildDir/Dockerfile. -// -// The image is built with buildDir as it's build context and will copy in the -// contents of buildDir/app into /app. -func (i *ImageCmd) Execute(ctx context.Context, out Outputter) error { - image := StackImage(i.Image, i.Stack) - appDir := filepath.Join(i.BuildDir, buildpack.AppDir) - - dockerfile, err := Dockerfile(image, appDir, i.Command) - if err != nil { - return fmt.Errorf("failed template Dockerfile: %w", err) - } - - dockerfilePath := filepath.Join(i.BuildDir, "Dockerfile") - - // #nosec G306 - if err := os.WriteFile(dockerfilePath, dockerfile, 0644); err != nil { - return fmt.Errorf("failed to create Dockerfile: %w", err) - } - - if i.NoBuild { - return nil - } - - dockerBuild := exec.CommandContext(ctx, "docker", "build", - "--quiet", - "--file", dockerfilePath, - i.BuildDir, - ) // #nosec G204 - dockerBuild.Stderr, dockerBuild.Stdout = out.ErrOrStderr(), out.OutOrStdout() - - return dockerBuild.Run() -} diff --git a/shared.go b/shared.go index 6abdc836..f7058fd7 100644 --- a/shared.go +++ b/shared.go @@ -17,16 +17,6 @@ import ( "github.com/spf13/cobra" ) -const ( - // StackReplacePattern is used to replace the stack name (e.g. heroku-20) - // during slugcmplr work. - StackReplacePattern = "%stack%" - - // StackNumberReplacePattern is used to replace the stack number (e.g. 20) - // during slugcmplr work. - StackNumberReplacePattern = "%stack-number%" -) - // BuildpackReference is a reference to a buildpack, containing its raw URL and // Name. type BuildpackReference struct { @@ -82,24 +72,6 @@ func (o *StdOutputter) ErrOrStderr() io.Writer { return o.Err } -// StackImage builds an image name for the given stack. -// -// stack is expected to be in the form `heroku-N` where N is the stack number -// (e.g. 18, 20). -// -// img may container either `%stack%` or `%stack-number%` which will be -// replaced by StackImage with the full stack name or only the number -// accordingly. -func StackImage(img, stack string) string { - stackNumber := strings.TrimPrefix(stack, "heroku-") - - return strings.ReplaceAll( - strings.ReplaceAll(img, StackReplacePattern, stack), - StackNumberReplacePattern, - stackNumber, - ) -} - // Commit attempts to return the current resolved HEAD commit for the git // repository at dir. func Commit(dir string) (string, error) {