Skip to content

Commit

Permalink
Build docker image.
Browse files Browse the repository at this point in the history
  • Loading branch information
monopole committed Aug 9, 2024
1 parent 7848a9a commit dbdf305
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 49 deletions.
9 changes: 5 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ MYGOBIN = $(shell go env GOPATH)/bin
endif

# Perform a local build.
# To build an actual release, use the release target.
# To build an actual release, use the 'release' target instead.
$(MYGOBIN)/mdrip:
releasing/buildWorkspace.sh
releasing/build.sh

# Run an end-to-end test.
.PHONY: testE2E
Expand All @@ -31,9 +31,10 @@ clean:
go clean -testcache

# Force serial execution of dependencies.
# This only really matters in the release target.
# This only matters for build targets that declare multiple dependencies,
# and it forces those dependencies to be built serially in the order that
# they appear in the dependencies list.
.NOTPARALLEL:

# Create a draft release and push it to github.
# Requires go, git, zip, tar, gh (github cli) and env var GH_TOKEN.
# Complains if workspace is dirty, tests fail, tags don't make sense, etc.
Expand Down
27 changes: 19 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,29 @@ the corresponding code block to `tmux` via its api.
<a href="assets/mdripDemo.png" target="_blank">
<img src="assets/mdripDemo.png"
alt="mdrip screenshot" width="95%" height="auto">
alt="mdrip screenshot" width="100%" height="auto">
</a>
## Installation
## Installation options
Install via the [Go](https://golang.org/dl) tool:
<!-- @installation -->
```
go install github.com/monopole/mdrip/v2@latest
```
or download a build from the [release page].
* Download a build from the [release page].
* Install via the [Go](https://golang.org/dl) tool:
<!-- @installation -->
```
go install github.com/monopole/mdrip/v2@latest
```
* Run via `docker`:
```
image=monopole/mdrip:latest
docker run $image version # try 'help' instead of 'version' to see options.
docker run \
--publish 8080:8080 \
--mount type=bind,source=`pwd`,target=/mnt \
$image serve /mnt
```
## Basic Extraction and Testing
Expand Down
12 changes: 0 additions & 12 deletions releasing/Dockerfile

This file was deleted.

6 changes: 4 additions & 2 deletions releasing/buildWorkspace.sh → releasing/build.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/bin/bash

# Build the current workspace, injecting loader flag values
# so that the version command works.
# Build the current workspace, i.e. whatever exists on local disk
# in whatever state, injecting loader flag values so that the 'version'
# command works and shows a dirty, modified state.

ldPath=github.com/monopole/mdrip/v2/internal/provenance

Expand All @@ -15,6 +16,7 @@ version=$(git describe --tags --always --dirty)
gitCommit="$(git branch --show-current)-modified"

out=$(go env GOPATH)/bin/mdrip

/bin/rm -f $out

go build \
Expand Down
1 change: 1 addition & 0 deletions releasing/cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Warning - This isn't being maintained and likely doesn't work.
steps:
- name: 'gcr.io/cloud-builders/go'
args: ['install', '-a', '-ldflags', "'-s'", '-installsuffix', 'cgo', '.']
Expand Down
118 changes: 118 additions & 0 deletions releasing/internal/dockerrunner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package internal

import (
"log/slog"
"os"
"path/filepath"
"strings"
"time"
)

// DockerRunner runs some "docker" commands.
type DockerRunner struct {
rn *MyRunner
ldVars *LdVars
pgmName string
dirTmp string
}

const (
imageRegistry = "hub.docker.com"
imageOwner = "monopole"

dockerTemplate = `
# This file is generated; DO NOT EDIT.
FROM golang:1.22.5-bullseye
WORKDIR /go/src/github.com/monopole/{{PGMNAME}}
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOWORK=off \
go build -v -o /go/bin/{{PGMNAME}} \
-ldflags "{{LDFLAGS}}" \
.
ENTRYPOINT ["/go/bin/{{PGMNAME}}"]
`
)

func NewDockerRunner(dirSrc, dirTmp string, ldVars *LdVars) *DockerRunner {
return &DockerRunner{
rn: NewMyRunner("docker", dirSrc, DoIt, 3*time.Minute),
ldVars: ldVars,
dirTmp: dirTmp,
pgmName: filepath.Base(dirSrc),
}
}

func (dr *DockerRunner) Content() []byte {
content := strings.Replace(
dockerTemplate[1:], "{{LDFLAGS}}", dr.ldVars.MakeLdFlags(), -1)
content = strings.Replace(content, "{{PGMNAME}}", dr.pgmName, -1)
return []byte(content)
}

func (dr *DockerRunner) ImageName() string {
return imageOwner + "/" + dr.pgmName
}

func (dr *DockerRunner) Build() error {
dr.rn.comment("building docker image at tag " + dr.ldVars.Version())
dockerFileName := filepath.Join(dr.dirTmp, "Dockerfile")
if err := os.WriteFile(dockerFileName, dr.Content(), 0644); err != nil {
return err
}
slog.Info("Wrote", "file", dockerFileName)
err := dr.rn.run(
NoHarmDone,
"build",
"--file", dockerFileName,
// "--platform", "linux/amd64,linux/arm64" (not using this yet)
"-t", dr.ImageName()+":"+dr.ldVars.Version(),
".",
)
if err != nil {
slog.Error(err.Error())
slog.Error(dr.rn.Out())
return err
}
return nil
}

func (dr *DockerRunner) Push() error {
err := dr.rn.run(
UndoIsHard,
"push",
dr.ImageName()+":"+dr.ldVars.Version(),
)
if err != nil {
slog.Error(err.Error())
slog.Error(dr.rn.Out())
return err
}
err = dr.rn.run(
UndoIsHard,
"push",
dr.ImageName()+":"+"latest",
)
if err != nil {
slog.Error(err.Error())
slog.Error(dr.rn.Out())
}
return err
}

func (dr *DockerRunner) Login() error {
dur := dr.rn.duration
dr.rn.duration = 3 * time.Second
err := dr.rn.run(
NoHarmDone,
"login",
)
dr.rn.duration = dur
if err != nil {
slog.Error(err.Error())
slog.Error(dr.rn.Out())
}
return err
}
2 changes: 1 addition & 1 deletion releasing/internal/ghrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"time"
)

// GithubRunner runs some the "gh" commands.
// GithubRunner runs some "gh" commands.
type GithubRunner struct {
rn *MyRunner
}
Expand Down
8 changes: 4 additions & 4 deletions releasing/internal/gobuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (gb *GoBuilder) Build(myOs EnumOs, myArch EnumArch) (string, error) {
if err := gb.goRun.run(NoHarmDone,
"build",
"-o", binary,
"-ldflags", gb.ldVars.makeLdFlags(),
"-ldflags", gb.ldVars.MakeLdFlags(),
".", // Using the "." is why we need HOME defined.
); err != nil {
return name, err
Expand All @@ -82,7 +82,7 @@ func (gb *GoBuilder) packageIt(
myOs EnumOs, myArch EnumArch, fileName string) (string, error) {
base := strings.Join([]string{
gb.pgmName,
gb.ldVars.version(),
gb.ldVars.Version(),
myOs.String(),
myArch.String(),
}, "_")
Expand Down Expand Up @@ -119,7 +119,7 @@ func (ldv *LdVars) makeDefinitions() []string {
return result
}

func (ldv *LdVars) makeLdFlags() string {
func (ldv *LdVars) MakeLdFlags() string {
result := []string{
"-s", // disable symbol table (small binary)
"-w", // disable DWARF generation (ditto)
Expand All @@ -131,7 +131,7 @@ func (ldv *LdVars) makeLdFlags() string {
return strings.Join(result, " ")
}

func (ldv *LdVars) version() string {
func (ldv *LdVars) Version() string {
v, ok := ldv.Kvs["version"]
if !ok {
panic("version not in ldFlags!")
Expand Down
31 changes: 31 additions & 0 deletions releasing/internal/gobuilder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package internal

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestLdVars_MakeLdFlags(t *testing.T) {
tests := map[string]struct {
ImportPath string
Kvs map[string]string
want string
}{
"t1": {
ImportPath: "github.com/foo/bar/provenance",
Kvs: map[string]string{"fruit": "apple", "animal": "dog"},
want: `-s -w ` +
`-X github.com/foo/bar/provenance.fruit=apple ` +
`-X github.com/foo/bar/provenance.animal=dog`,
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
ldv := &LdVars{
ImportPath: tc.ImportPath,
Kvs: tc.Kvs,
}
assert.Equalf(t, tc.want, ldv.MakeLdFlags(), "MakeLdFlags()")
})
}
}
64 changes: 46 additions & 18 deletions releasing/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ import (
"github.com/monopole/mdrip/releasing/internal"
)

// This builds and releases a module to github.
// - While running, the process's working directory must be the
// same as the repo from which code is being released.
// - The repo should have one go.mod at the top; that's what's being released.
// - The desired release tag should have already been applied.
// This builds and releases a Go module to GitHub and dockerhub.
//
// In the old days I'd have hacked this up in unreadable bash code.
// Here I'm using Go to make it more readable and robust.
//
// While running, the process's working directory must be the
// same as the top of the repo from which code is being released.
// Further, the repo should have one go.mod at the top; that's what's
// being released.
//
// The desired release tag should have already been applied
// to the local repo.
func main() {
if os.Getenv("GH_TOKEN") == "" {
log.Fatal("GH_TOKEN not defined, so the gh tool won't work.")
Expand All @@ -30,6 +37,10 @@ func main() {
if !filepath.IsAbs(dirSrc) {
log.Fatal(dirSrc + " is not an absolute path.")
}
doIt(dirSrc)
}

func doIt(dirSrc string) {
var (
tag, commit, dirOut string
err error
Expand All @@ -56,7 +67,21 @@ func main() {
if err != nil {
log.Fatal(err)
}
assets, err = buildReleaseAssets(dirSrc, dirOut, tag, commit)
buildDate := time.Now().UTC()

ldVars := &internal.LdVars{
ImportPath: "github.com/monopole/mdrip/v2/internal/provenance",
Kvs: map[string]string{
"version": tag,
"gitCommit": commit,
"buildDate": buildDate.Format(time.RFC3339),
},
}
err = buildAndPushDockerImage(dirSrc, dirOut, ldVars)
if err != nil {
log.Fatal(err)
}
assets, err = buildReleaseAssetsForGitHub(dirSrc, dirOut, ldVars)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -95,18 +120,9 @@ func findTag(git *internal.GitRunner) (string, string, error) {
return tag, commitHead, err
}

func buildReleaseAssets(
dirSrc, dirOut, tag, commitHash string) ([]string, error) {
goBuilder := internal.NewGoBuilder(
dirSrc, dirOut,
&internal.LdVars{
ImportPath: "github.com/monopole/mdrip/v2/internal/provenance",
Kvs: map[string]string{
"version": tag,
"gitCommit": commitHash,
"buildDate": time.Now().UTC().Format(time.RFC3339),
},
})
func buildReleaseAssetsForGitHub(
dirSrc, dirOut string, ldVars *internal.LdVars) ([]string, error) {
goBuilder := internal.NewGoBuilder(dirSrc, dirOut, ldVars)
var assetPaths []string
for _, pair := range []struct {
myOs internal.EnumOs
Expand All @@ -127,3 +143,15 @@ func buildReleaseAssets(
}
return assetPaths, nil
}

func buildAndPushDockerImage(
dirSrc, dirOut string, ldVars *internal.LdVars) error {
docker := internal.NewDockerRunner(dirSrc, dirOut, ldVars)
if err := docker.Login(); err != nil {
return err
}
if err := docker.Build(); err != nil {
return err
}
return docker.Push()
}

0 comments on commit dbdf305

Please sign in to comment.