Skip to content

Commit

Permalink
Merge branch 'main' into triggers
Browse files Browse the repository at this point in the history
  • Loading branch information
mamort committed Apr 13, 2024
2 parents eb49591 + fb0fe1e commit 03fa1bc
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 45 deletions.
28 changes: 16 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@ 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: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Test
run: go test ./...
- 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: true
tags: ghcr.io/${{ github.repository }}:latest
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -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"
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
116 changes: 95 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -74,26 +76,97 @@ 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`).

## Build Docker image

6. TODO: 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.

7. TODO: Verify build and test
```yml
on:
push
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
```

8. TODO: Docker image & registry
> [!NOTE]
>
> The `${{ <expression> }}` 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/<user-or-org>/<repo-name>:latest`.

Creating workflow:
* Creating a Docker image & pis
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

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"
```

Verifying:
* Testing pushing of verify image ends up in Docker registry
You'll have to checkout the code repository and setup go, similarly to before.

## Testing and linting PRs
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
* Triggers, how do they work?

## Triggering workflows

Expand All @@ -108,7 +181,7 @@ Repository related events are the most common and are triggered when something h

```
on: push # Triggers when a push is made to the repository
on: pull_request # Triggers when a pull request is opened
on: pull_request # Triggers when a pull request is opened or changed
on: workflow_dispatch # Triggers when a user manually requests a workflow to run
```

Expand All @@ -124,11 +197,12 @@ on:
**Tasks**
1. Update the `build-triggers.yml` workflow and add the event for triggering the workflow when a PR is created
2. Create a new branch based on main and create a new PR. Verify that the workflow is run.
3. Update the `build-triggers.yml` workflow and add the event for triggering the workflow manually
4. Go to the [GitHub Actions page of the workflow](/actions/workflows/build-triggers.yml) and verify that the workflow can be run manually
1. Rewrite the docker build workflow `build.yml` to only be done on main and rewrite the build and lint workflow `test.yml` to only run on PR changes
2. Create a new feature branch, add a new commit with a dummy change (to any file) and finally create a PR to main. Verify that the `test.yml` workflow is run on the feature branch. Merge the PR and verify that the `build.yml`-workflow is only run on the main-branch.
3. Update the `test.yml` workflow and add the event for triggering the workflow manually. Make sure to push the change to main-branch.
4. Go to the [GitHub Actions page of the workflow](/actions/workflows/test.yml) and verify that the workflow can be run manually
## Manually triggering workflows
TODO:
* `on: workflow_dispatch`, run a job on a given branch
Expand Down
24 changes: 12 additions & 12 deletions internal/greeting/greet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
}
}

0 comments on commit 03fa1bc

Please sign in to comment.