From f4f6635a52db560f26c673b8678103c42680a60d Mon Sep 17 00:00:00 2001 From: Ole Kristian Pedersen Date: Thu, 11 Apr 2024 20:44:00 +0200 Subject: [PATCH 1/6] Add Dockerfile --- .github/workflows/build.yml | 8 ++++++++ Dockerfile | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 Dockerfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4cf7c69..7eea64b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,3 +20,11 @@ jobs: - name: Test run: go test ./... + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + push: false + tags: ${{ github.repository }}:latest diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..63a8d4f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM golang:alpine3.19@sha256:cdc86d9f363e8786845bea2040312b4efa321b828acdeb26f393faa864d887b0 AS build + +WORKDIR /app + +# Pre-compile std lib, can be cached! +RUN CGO_ENABLED=0 GOOS=linux go install -v -installsuffix cgo -a std + +COPY go.mod ./ +RUN go mod download && go mod verify + +COPY ./cmd ./cmd +COPY ./internal ./internal +RUN go build -v -o /app/bin/main ./cmd/api/main.go +RUN CGO_ENABLED=0 GOOS=linux go build -v -installsuffix cgo -o /app/bin/main -ldflags "-s -w" ./cmd/api/main.go + +FROM scratch + +COPY --from=build /app/bin/main /app/bin/main + +EXPOSE 8888 + +CMD ["/app/bin/main"] From bcf5728b0b0596519fe4d91fc63ff0f129293e7f Mon Sep 17 00:00:00 2001 From: Ole Kristian Pedersen Date: Thu, 11 Apr 2024 20:48:37 +0200 Subject: [PATCH 2/6] Enable push --- .github/workflows/build.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7eea64b..6696a87 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,8 @@ on: jobs: build: + permissions: + packages: write runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v4 @@ -23,8 +25,15 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + - name: Build and push Docker image uses: docker/build-push-action@v5 with: - push: false - tags: ${{ github.repository }}:latest + push: true + tags: ghcr.io/${{ github.repository }}:latest From 1c03ab17874ee73df25aa2145b32cd2c2bb7f296 Mon Sep 17 00:00:00 2001 From: Ole Kristian Pedersen Date: Sat, 13 Apr 2024 18:46:58 +0200 Subject: [PATCH 3/6] Update task descriptions --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index fae69e3..504d8c6 100644 --- a/README.md +++ b/README.md @@ -20,20 +20,20 @@ TODO: Likely `gh repo fork --clone` or using the GitHub UI to fork the repo to a This repository contains a simple go app. You do not need to know go, nor use any golang tooling. We will, unless explicitly specified otherwise, only modify files in the special `.github/` directory. -## Building and testing the code w/jobs and steps +## Our first wokflow -1. We'll start with a simple workflow. Create the file `.github/workflows/build.yml` and with the following content: +1. We'll start with a simple workflow. Create the file `.github/workflows/test.yml` and with the following content: ```yml # The "display name", shown in the GitHub UI - name: Build + name: Build and test # Trigger, run on push on any branch on: push: jobs: - build: # The 'build' job + test: # The 'build' job name: "Build application" runs-on: 'ubuntu-latest' steps: @@ -58,7 +58,9 @@ This repository contains a simple go app. You do not need to know go, nor use an > * `steps:` run sequentially, and might run shell scripts or an action (a reusable, pre-made piece of code). Each step can run conditionally. If a step fails, all later steps fail by default (this is overrideable). -4. Let's use some pre-made actions to checkout our code, and install golang tooling. Replace the "hello world" step with the following steps: +## Build and test the application + +1. Let's use some pre-made actions to checkout our code, and install golang tooling. Replace the "hello world" step with the following steps: ```yml # Checkout code @@ -74,27 +76,90 @@ This repository contains a simple go app. You do not need to know go, nor use an - run: go version ``` -5. Again, run `actionlint` before you commit and push. Verify that the correct version is printed. +2. Again, run `actionlint` before you commit and push. Verify that the correct version is printed. + +3. Continue by adding steps to build and test the application: + + ```yml + - name: Build + run: go build -v ./... + + - name: Test + run: go test ./... + ``` + +4. Verify that the workflow fails if the build fails (create a syntax error in any file). Separately, verify that the workflow fail when the tests are incorrect (modify a test case in `internal/greeting/greet_test.go`). -6. TODO: Build and test +## Build Docker image -7. TODO: Verify build and test +1. In order to do a container-based deploy. A `Dockerfile` is in the root directory. We'll use actions provided by Docker to build the image. -8. TODO: Docker image & registry + ```yml + on: + push -Creating workflow: -* Creating a Docker image & pis + jobs: + build: + steps: + - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + push: false + tags: ghcr.io/${{ github.repository }}:latest + ``` -Verifying: -* Testing pushing of verify image ends up in Docker registry + > [!NOTE] + > + > The `${{ }}` syntax is used to access variables, call functions and more. You can read more in [the documentation](https://docs.github.com/en/actions/learn-github-actions/expressions). + > + > In this case, `${{ github.repository }}` is a variable from the [`github` context](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context) refers to the owner or and repos, meaning the Docker image will be tagged with `ghcr.io//:latest`. -## Testing and linting PRs +2. Push and verify that the action runs correctly. + +3. In order to push the image, we will need to set up [permissions](https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs). With `packages: write` you allow the action to push images to the GitHub Container Registry (GHCR). You can set it at the top-level, for all jobs in the workflow, or for a single job: + + ```yml + jobs: + build: + permissions: + packages: write + # ... runs-on, steps, etc + ``` + +4. We'll have to add a step the `docker/login-action@v3` action to login to GHCR, before we push it. Add the following step before the build and push step: + + ```yml + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + ``` + + > [!NOTE] + > The `github.token` (often referred to as `GITHUB_TOKEN`) is a special token used to authenticate the workflow job. Read more about it here in [the documentation](https://docs.github.com/en/actions/security-guides/automatic-token-authentication). + +5. Finally, modify the build and push step. Set `push: true` to make the action push the image after it's built. + +6. Push the changes and make sure the workflow runs successfully. This will push the package to your organization's or your own package registry. The image should be associated with your repo as a package (right-hand side on the main repository page). + +## Parallel jobs TODO: * Build, test & lint for each push to PR * Require build, tests & linting to succeed to merge PR + +## Introduction to triggers + + * Triggers, how do they work? +Tasks: 1) Rewrite docker build to only be done on main 2) do build and lint on PR only ## Manually triggering workflows TODO: From 2752aef9a743b6a3fdbcd3822dfbd76e117eec92 Mon Sep 17 00:00:00 2001 From: Ole Kristian Pedersen Date: Sat, 13 Apr 2024 18:47:15 +0200 Subject: [PATCH 4/6] Update app formatting --- internal/greeting/greet_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/greeting/greet_test.go b/internal/greeting/greet_test.go index 690f4d6..620f53e 100644 --- a/internal/greeting/greet_test.go +++ b/internal/greeting/greet_test.go @@ -4,7 +4,7 @@ import "testing" func TestGreetOneName(t *testing.T) { name := "Espen Askeladd" - names := []string{name} + names := []string{name} want := "Hello Espen Askeladd" got, err := Greet(names) @@ -17,7 +17,7 @@ func TestGreetOneName(t *testing.T) { } func TestGreetTwoNames(t *testing.T) { - names := []string{"Per", "Espen Askeladd"} + names := []string{"Per", "Espen Askeladd"} want := "Hello Per and Espen Askeladd" got, err := Greet(names) @@ -30,7 +30,7 @@ func TestGreetTwoNames(t *testing.T) { } func TestGreetThreeNames(t *testing.T) { - names := []string{"Per", "Pål", "Espen Askeladd"} + names := []string{"Per", "Pål", "Espen Askeladd"} want := "Hello Per, Pål and Espen Askeladd" got, err := Greet(names) @@ -43,7 +43,7 @@ func TestGreetThreeNames(t *testing.T) { } func TestGreetManyNames(t *testing.T) { - names := []string{"Hans", "Grete", "Per", "Pål", "Espen Askeladd"} + names := []string{"Hans", "Grete", "Per", "Pål", "Espen Askeladd"} want := "Hello Hans, Grete, Per, Pål and Espen Askeladd" got, err := Greet(names) @@ -56,13 +56,13 @@ func TestGreetManyNames(t *testing.T) { } func TestGreetNone(t *testing.T) { - names := []string{} + names := []string{} - got, err := Greet(names) - if err == nil { - t.Fatalf("Expected returned error, got nil") - } - if got != "" { - t.Fatalf("Expected empty string as return value with the error, got '%s'", got) - } + got, err := Greet(names) + if err == nil { + t.Fatalf("Expected returned error, got nil") + } + if got != "" { + t.Fatalf("Expected empty string as return value with the error, got '%s'", got) + } } From 401d51d44c7f276bd6d6fea6e6801f349b995458 Mon Sep 17 00:00:00 2001 From: Ole Kristian Pedersen Date: Sat, 13 Apr 2024 18:47:38 +0200 Subject: [PATCH 5/6] Update the workflows --- .github/workflows/build.yml | 15 +-------------- .github/workflows/test.yml | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6696a87..e1a0de5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,25 +2,12 @@ on: push: jobs: - build: + test: permissions: packages: write runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v4 - - name: Setup go - uses: actions/setup-go@v4 - with: - go-version: '1.21.x' - - #- run: echo "Hello world" - #- run: go version - - - name: Build - run: go build -v ./... - - - name: Test - run: go test ./... - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e6ffb26 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,37 @@ +on: + push: + +jobs: + test: + runs-on: 'ubuntu-latest' + steps: + - uses: actions/checkout@v4 + - name: Setup go + uses: actions/setup-go@v4 + with: + go-version: '1.21.x' + + #- run: echo "Hello world" + #- run: go version + + - name: Build + run: go build -v ./... + + - name: Test + run: go test ./... + + lint: + name: Lint + runs-on: 'ubuntu-latest' + steps: + - uses: actions/checkout@v4 + + - name: Setup go + uses: actions/setup-go@v4 + with: + go-version: '1.21.x' + + - name: Verify formatting + run: | + no_unformatted_files="$(gofmt -l $(git ls-files '*.go') | wc -l)" + exit "$no_unformatted_files" From fb0fe1e00b51d87e5b462b64a4990b4f190361c8 Mon Sep 17 00:00:00 2001 From: Ole Kristian Pedersen Date: Sat, 13 Apr 2024 18:58:53 +0200 Subject: [PATCH 6/6] Add lint task --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 504d8c6..6b33b8e 100644 --- a/README.md +++ b/README.md @@ -150,8 +150,22 @@ This repository contains a simple go app. You do not need to know go, nor use an ## Parallel jobs +Jobs in the same workflow file can be run in parallel if they're not dependent on each other. + +1. We also want to lint the app code. In order to get faster feedback, we can lint create a separate job in our `test.yml` workflow file. Create a new job in `test.yml` for linting, which contains the following step: + + ```yml + - name: Verify formatting + run: | + no_unformatted_files="$(gofmt -l $(git ls-files '*.go') | wc -l)" + exit "$no_unformatted_files" + ``` + + You'll have to checkout the code repository and setup go, similarly to before. + +2. Push the code and verify that the workflow runs two jobs successfully. + TODO: -* Build, test & lint for each push to PR * Require build, tests & linting to succeed to merge PR ## Introduction to triggers