Skip to content

Commit

Permalink
Merge pull request #26 from sapcc/dockerfile
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperSandro2000 committed Jun 7, 2022
2 parents 7e96523 + 6ce321d commit 0f0a075
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 29 deletions.
2 changes: 2 additions & 0 deletions Makefile.maker.yaml
@@ -1,3 +1,5 @@
# Configuration file for <https://github.com/sapcc/go-makefile-maker>

binaries:
- name: go-makefile-maker
fromPackage: .
Expand Down
44 changes: 39 additions & 5 deletions README.md
Expand Up @@ -2,7 +2,7 @@

[![CI](https://github.com/sapcc/go-makefile-maker/actions/workflows/ci.yaml/badge.svg)](https://github.com/sapcc/go-makefile-maker/actions/workflows/ci.yaml)

Generates a Makefile and GitHub workflows for your Go application:
Generates a Makefile and optionally also GitHub workflows and a Dockerfile for your Go application:

* Makefile follows established Unix conventions for installing and packaging,
and includes targets for vendoring, running tests and checking code quality.
Expand All @@ -29,7 +29,7 @@ $ go-makefile-maker

`go-makefile-maker` also generates a `help` target for usage info:

```
```sh
$ make help
```

Expand All @@ -43,8 +43,10 @@ Take a look at `go-makefile-maker`'s [own config file](./Makefile.maker.yaml) fo

The config file has the following sections:

* [metadata](#metadata)
* [binaries](#binaries)
* [coverageTest](#coveragetest)
* [docker](#docker)
* [variables](#variables)
* [vendoring](#vendoring)
* [golangciLint](#golangcilint)
Expand All @@ -58,6 +60,17 @@ The config file has the following sections:
* [githubWorkflow\.license](#githubworkflowlicense)
* [githubWorkflow\.spellCheck](#githubworkflowspellcheck)

### `metadata`

```yaml
metadata:
url: https://github.com/foo/bar
```

`metadata` contains information about the project which cannot be guessed consistently:

- `url` is the repository's remote URL.

### `binaries`

```yaml
Expand Down Expand Up @@ -88,6 +101,28 @@ By default, all packages inside the repository are subject to coverage testing,
The values in `only` and `except` are regexes for `grep -E`.
Since only entire packages (not single source files) can be selected for coverage testing, the regexes have to match package names, not on file names.

### `docker`

```yaml
dockerfile:
enabled: true
entrypoint: { "/bin/bash", "--", "--arg" }
extraIgnores:
- tmp
- files
extraPackages:
- curl
- openssl
user: root
```

When `enabled`, go-makefile-maker will generate a `Dockerfile` and a `.dockerignore` file.

- `entrypoint` allows overwriting the final entrypoint.
- `extraIgnores` appends entries in `.dockerignore` to the default ones.
- `extraPackages` installs extra Alpine packages in the final Docker layer. `ca-certificates` is always installed.
- `user` changes the arguments given to the Docker `USER` command. The default value is `nobody`.

### `variables`

```yaml
Expand Down Expand Up @@ -277,17 +312,16 @@ image tag.

If `securityChecks` is enabled then it will generate two workflows:

- [CodeQL] workflow will run [CodeQL], GitHub's industry-leading semantic code analysis
* [CodeQL] workflow will run [CodeQL], GitHub's industry-leading semantic code analysis
engine, on your source code to find security vulnerabilities. You can see the security
report generated by CodeQL under your repo's Security tab.

In addition to running the workflow when new code is pushed, this workflow will also run
on a weekly basis (every Monday at 07:00 AM) so that existing code can be checked for
new vulnerabilities.
- [dependency-review] workflow will scan your pull requests for dependency changes and
* [dependency-review] workflow will scan your pull requests for dependency changes and
will raise an error if any new dependencies have existing vulnerabilities.


```yaml
securityChecks:
enabled: true
Expand Down
15 changes: 15 additions & 0 deletions internal/core/config.go
Expand Up @@ -46,6 +46,8 @@ type Configuration struct {
SpellCheck SpellCheckConfiguration `yaml:"spellCheck"`
GitHubWorkflow *GithubWorkflowConfiguration `yaml:"githubWorkflow"`
Renovate RenovateConfig `yaml:"renovate"`
Dockerfile DockerfileConfig `yaml:"dockerfile"`
Metadata Metadata `yaml:"metadata"`
}

//Variable returns the value of this variable if it's overridden in the config,
Expand Down Expand Up @@ -156,6 +158,19 @@ type RenovateConfig struct {
GoVersion string `yaml:"goVersion"`
}

// DockerfileConfig appears in type Configuration.
type DockerfileConfig struct {
Enabled bool `yaml:"enabled"`
Entrypoint []string `yaml:"entrypoint"`
ExtraIgnores []string `yaml:"extraIgnores"`
ExtraPackages []string `yaml:"extraPackages"`
User string `yaml:"user"`
}

type Metadata struct {
URL string `yaml:"url"`
}

///////////////////////////////////////////////////////////////////////////////
// Helper functions

Expand Down
4 changes: 4 additions & 0 deletions internal/dockerfile/Dockerfile.template
@@ -0,0 +1,4 @@
# renovate: datasource=docker depName=alpine versioning=docker
ARG ALPINE_VERSION=3.16
# renovate: datasource=docker depName=golang versioning=docker
ARG GOLANG_VERSION=1.17.10-alpine
114 changes: 114 additions & 0 deletions internal/dockerfile/docker.go
@@ -0,0 +1,114 @@
// Copyright 2022 SAP SE
//
// 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.

package dockerfile

import (
"fmt"
"os"
"strconv"
"strings"

_ "embed"

"github.com/sapcc/go-makefile-maker/internal/core"
"github.com/sapcc/go-makefile-maker/internal/util"
)

func mustI(_ int, err error) {
util.Must(err)
}

//go:embed Dockerfile.template
var template []byte

func RenderConfig(cfg core.Configuration) error {
var goBuildflags, packages, user, entrypoint string

if cfg.Vendoring.Enabled {
goBuildflags = ` GO_BUILDFLAGS='-mod vendor'`
}

for _, v := range append([]string{"ca-certificates"}, cfg.Dockerfile.ExtraPackages...) {
packages += fmt.Sprintf(" %s", v)
}

if cfg.Dockerfile.User != "" {
user = cfg.Dockerfile.User
} else {
user = "nobody"
}

if len(cfg.Dockerfile.Entrypoint) > 0 {
entrypoint = strconv.Quote(strings.Join(cfg.Dockerfile.Entrypoint, `", "`))
} else {
entrypoint = fmt.Sprintf(`"/usr/bin/%s"`, cfg.Binaries[0].Name)
}

dockerfile := fmt.Sprintf(
`%[1]s
FROM golang:${GOLANG_VERSION}${ALPINE_VERSION} as builder
RUN apk add --no-cache gcc git make musl-dev
COPY . /src
RUN make -C /src install PREFIX=/pkg%[2]s
################################################################################
FROM alpine:${ALPINE_VERSION}
RUN apk add --no-cache%[4]s
COPY --from=builder /pkg/ /usr/
LABEL source_repository="%[3]s" \
org.opencontainers.image.url="%[3]s" \
org.opencontainers.image.revision=unknown
USER %[5]s:%[5]s
WORKDIR /var/empty
ENTRYPOINT [ %[6]s ]
`, template, goBuildflags, cfg.Metadata.URL, packages, user, entrypoint)

f, err := os.Create("Dockerfile")
util.Must(err)

mustI(f.WriteString(dockerfile))
util.Must(f.Close())

f, err = os.Create(".dockerignore")
util.Must(err)

mustI(f.WriteString(
`.dockerignore
# TODO: uncomment when applications no longer use git to get version information
#.git/
.github/
.gitignore
.goreleaser.yml
/*.env*
.golangci.yaml
build/
CONTRIBUTING.md
Dockerfile
docs/
LICENSE*
Makefile.maker.yaml
README.md
report.html
shell.nix
/testing/
`))
mustI(f.WriteString(strings.Join(cfg.Dockerfile.ExtraIgnores, "\n") + "\n"))
return f.Close()
}
30 changes: 30 additions & 0 deletions internal/util/util.go
@@ -0,0 +1,30 @@
// Copyright 2022 SAP SE
//
// 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.

package util

import (
"fmt"
"os"
)

func Must(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL:", err.Error())
if os.IsNotExist(err) {
fmt.Fprintln(os.Stderr, "HINT: Did you run this in the root directory of a suitable Git repository?")
}
os.Exit(1)
}
}
47 changes: 23 additions & 24 deletions main.go
Expand Up @@ -20,79 +20,78 @@ package main

import (
"errors"
"fmt"
"os"

"golang.org/x/mod/modfile"
"gopkg.in/yaml.v3"

"github.com/sapcc/go-makefile-maker/internal/core"
"github.com/sapcc/go-makefile-maker/internal/dockerfile"
"github.com/sapcc/go-makefile-maker/internal/ghworkflow"
"github.com/sapcc/go-makefile-maker/internal/golangcilint"
"github.com/sapcc/go-makefile-maker/internal/makefile"
"github.com/sapcc/go-makefile-maker/internal/renovate"
"github.com/sapcc/go-makefile-maker/internal/util"
)

func main() {
file, err := os.Open("Makefile.maker.yaml")
must(err)
util.Must(err)

var cfg core.Configuration
dec := yaml.NewDecoder(file)
dec.KnownFields(true)
must(dec.Decode(&cfg))
must(file.Close())
must(cfg.Validate())
util.Must(dec.Decode(&cfg))
util.Must(file.Close())
util.Must(cfg.Validate())

// Render Makefile.
must(makefile.Render(&cfg))
util.Must(makefile.Render(&cfg))

// Read go.mod file for module path and Go version.
modFilename := "go.mod"
modFileBytes, err := os.ReadFile(modFilename)
must(err)
util.Must(err)
modFile, err := modfile.Parse(modFilename, modFileBytes, nil)
must(err)
util.Must(err)

// Render Dockerfile
if cfg.Dockerfile.Enabled {
if cfg.Metadata.URL == "" {
util.Must(errors.New("metadata.url needs to be set when docker.enabled is true"))
}
util.Must(dockerfile.RenderConfig(cfg))
}

// Render golangci-lint config file.
if cfg.GolangciLint.CreateConfig {
if modFile.Module.Mod.Path == "" {
must(errors.New("could not find module path from go.mod file, make sure it is defined"))
util.Must(errors.New("could not find module path from go.mod file, make sure it is defined"))
}
must(golangcilint.RenderConfig(cfg.GolangciLint, cfg.Vendoring.Enabled, modFile.Module.Mod.Path, cfg.SpellCheck.IgnoreWords))
util.Must(golangcilint.RenderConfig(cfg.GolangciLint, cfg.Vendoring.Enabled, modFile.Module.Mod.Path, cfg.SpellCheck.IgnoreWords))
}

// Render GitHub workflows.
if cfg.GitHubWorkflow != nil {
if cfg.GitHubWorkflow.Global.GoVersion == "" {
if modFile.Go.Version == "" {
must(errors.New("could not find Go version from go.mod file, consider defining manually by setting 'githubWorkflow.global.goVersion' in config"))
util.Must(errors.New("could not find Go version from go.mod file, consider defining manually by setting 'githubWorkflow.global.goVersion' in config"))
}
cfg.GitHubWorkflow.Global.GoVersion = modFile.Go.Version
}
must(ghworkflow.Render(&cfg))
util.Must(ghworkflow.Render(&cfg))
}

// Render Renovate config.
if cfg.Renovate.Enabled {
if cfg.Renovate.GoVersion == "" {
if modFile.Go.Version == "" {
must(errors.New("could not find Go version from go.mod file, consider defining manually by setting 'renovate.goVersion' in config"))
util.Must(errors.New("could not find Go version from go.mod file, consider defining manually by setting 'renovate.goVersion' in config"))
}
cfg.Renovate.GoVersion = modFile.Go.Version
}
// Only enable Renovate's github-actions manager for go-makefile-maker itself.
enableGHActions := len(cfg.Binaries) > 0 && cfg.Binaries[0].Name == "go-makefile-maker"
must(renovate.RenderConfig(cfg.GitHubWorkflow.Global.Assignees, cfg.Renovate.GoVersion, enableGHActions))
}
}

func must(err error) {
if err != nil {
fmt.Fprintln(os.Stderr, "FATAL:", err.Error())
if os.IsNotExist(err) {
fmt.Fprintln(os.Stderr, "HINT: Did you run this in the root directory of a suitable Git repository?")
}
os.Exit(1)
util.Must(renovate.RenderConfig(cfg.GitHubWorkflow.Global.Assignees, cfg.Renovate.GoVersion, enableGHActions))
}
}

0 comments on commit 0f0a075

Please sign in to comment.