Skip to content

Commit

Permalink
Implements go build compilation process
Browse files Browse the repository at this point in the history
Implementation of #3.

Signed-off-by: Timothy Hitchener <thitchener@vmware.com>
Signed-off-by: Ryan Moran <rmoran@vmware.com>
Signed-off-by: Sophie Wigmore <swigmore@vmware.com>
  • Loading branch information
ForestEckhardt authored and ryanmoran committed Jun 30, 2020
1 parent 18fa609 commit 867d713
Show file tree
Hide file tree
Showing 79 changed files with 9,509 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .github/util/tag.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

set -eu
set -o pipefail

function main() {
local previous tag
previous="$(git describe --tags "$(git rev-list --tags --max-count=1)")"
tag="$(printf "%s" "$previous" | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g')"

printf "v%s" "${tag#v}"
}

main "${@:-}"
94 changes: 94 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Create Release

on:
push:
branches:
- master

jobs:
unit:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.14
- name: Checkout
uses: actions/checkout@v2
- name: Run Unit Tests
run: ./scripts/unit.sh

integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: unit
steps:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.14
- name: Checkout
uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Run Integration Tests
run: ./scripts/integration.sh
env:
GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release:
name: Release
runs-on: ubuntu-latest
needs: integration
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Tag
id: tag
run: |
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
TAG="$(./.github/util/tag.sh)"
echo "::set-output name=tag::$TAG"
- name: Package
env:
TAG: ${{ steps.tag.outputs.tag }}
run: PACKAGE_DIR=artifact ./scripts/package.sh --version "${TAG}" --archive
- name: Create Release Notes
id: create-release-notes
run: |
mkdir -p "${HOME}/bin"
export PATH="${PATH}:${HOME}/bin"
curl "https://github.com/cloudfoundry/packit/releases/download/v0.0.4/jam-linux" \
--silent \
--location \
--output "${HOME}/bin/jam"
chmod +x "${HOME}/bin/jam"
RELEASE_BODY=$(jam summarize --buildpack "${PWD}/artifact.tgz" --format markdown)
# Coz of this messed up issue
# https://github.community/t5/GitHub-Actions/set-output-Truncates-Multiline-Strings/m-p/38372#M3322
RELEASE_BODY="${RELEASE_BODY//'%'/'%25'}"
RELEASE_BODY="${RELEASE_BODY//$'\n'/'%0A'}"
RELEASE_BODY="${RELEASE_BODY//$'\r'/'%0D'}"
echo "::set-output name=release_body::$RELEASE_BODY"
- name: Create Release
id: create-release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.tag.outputs.tag }}
release_name: ${{ steps.tag.outputs.tag }}
body: ${{ steps.create-release-notes.outputs.release_body }}
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create-release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: artifact.tgz
asset_name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.tag }}.tgz
asset_content_type: application/gzip

35 changes: 35 additions & 0 deletions .github/workflows/receive-github-config-updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Sync

on:
repository_dispatch:
types: working-dir-update

jobs:
build:
name: Sync
runs-on: ubuntu-latest
steps:

# checkout paketo github-config as src-repo
- name: Checkout paketo github-config repo
uses: actions/checkout@v2
with:
repository: paketo-buildpacks/github-config
path: config-repo

# checkout this repo
- name: Checkout
uses: actions/checkout@v2
with:
path: current-repo

- name: Run the sync action
uses: paketo-buildpacks/github-config/actions/sync@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
config-repo: config-repo
current-repo: current-repo
config-path: "/implementation"
ssh-private-key: ${{ secrets.PAKETO_BOT_SSH_KEY }}
id: do-sync
37 changes: 37 additions & 0 deletions .github/workflows/test-pull-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test Pull Request

on:
pull_request:
branches:
- master

jobs:
unit:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.14
- name: Checkout
uses: actions/checkout@v2
- name: Run Unit Tests
run: ./scripts/unit.sh

integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: unit
steps:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.14
- name: Checkout
uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Run Integration Tests
run: ./scripts/integration.sh
env:
GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.bin
Empty file added .packit
Empty file.
115 changes: 115 additions & 0 deletions build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package gobuild

import (
"errors"
"path/filepath"
"time"

"github.com/paketo-buildpacks/packit"
"github.com/paketo-buildpacks/packit/chronos"
)

//go:generate faux --interface BuildProcess --output fakes/build_process.go
type BuildProcess interface {
Execute(workspace, output, goPath, goCache string, targets []string) (command string, err error)
}

//go:generate faux --interface PathManager --output fakes/path_manager.go
type PathManager interface {
Setup(workspace string) (goPath, path string, err error)
Teardown(goPath string) error
}

//go:generate faux --interface ChecksumCalculator --output fakes/checksum_calculator.go
type ChecksumCalculator interface {
Sum(path string) (sha string, err error)
}

//go:generate faux --interface SourceRemover --output fakes/source_remover.go
type SourceRemover interface {
Clear(path string) error
}

func Build(
buildProcess BuildProcess,
pathManager PathManager,
clock chronos.Clock,
checksumCalculator ChecksumCalculator,
logs LogEmitter,
parser TargetsParser,
sourceRemover SourceRemover,
) packit.BuildFunc {

return func(context packit.BuildContext) (packit.BuildResult, error) {
logs.Title("%s %s", context.BuildpackInfo.Name, context.BuildpackInfo.Version)

targetsLayer, err := context.Layers.Get(TargetsLayerName, packit.LaunchLayer)
if err != nil {
return packit.BuildResult{}, err
}

goCacheLayer, err := context.Layers.Get(GoCacheLayerName, packit.CacheLayer)
if err != nil {
return packit.BuildResult{}, err
}

checksum, err := checksumCalculator.Sum(context.WorkingDir)
if err != nil {
return packit.BuildResult{}, err
}

previousSum, _ := targetsLayer.Metadata[WorkspaceSHAKey].(string)
if checksum != previousSum {
targets, err := parser.Parse(filepath.Join(context.WorkingDir, "buildpack.yml"))
if err != nil {
return packit.BuildResult{}, err
}

goPath, path, err := pathManager.Setup(context.WorkingDir)
if err != nil {
return packit.BuildResult{}, err
}

command, err := buildProcess.Execute(path, filepath.Join(targetsLayer.Path, "bin"), goPath, goCacheLayer.Path, targets)
if err != nil {
return packit.BuildResult{}, err
}

err = pathManager.Teardown(goPath)
if err != nil {
return packit.BuildResult{}, err
}

targetsLayer.Metadata = map[string]interface{}{
WorkspaceSHAKey: checksum,
"built_at": clock.Now().Format(time.RFC3339Nano),
"command": command,
}
}

command, ok := targetsLayer.Metadata["command"].(string)
if !ok {
return packit.BuildResult{}, errors.New("failed to identify start command from reused layer metadata")
}

err = sourceRemover.Clear(context.WorkingDir)
if err != nil {
return packit.BuildResult{}, err
}

logs.Process("Assigning launch processes")
logs.Subprocess("web: %s", command)

return packit.BuildResult{
Plan: context.Plan,
Layers: []packit.Layer{targetsLayer, goCacheLayer},
Processes: []packit.Process{
{
Type: "web",
Command: command,
Direct: context.Stack == TinyStackName,
},
},
}, nil
}
}
53 changes: 53 additions & 0 deletions build_targets_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package gobuild

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"gopkg.in/yaml.v2"
)

type BuildTargetsParser struct{}

func NewBuildTargetsParser() BuildTargetsParser {
return BuildTargetsParser{}
}

func (p BuildTargetsParser) Parse(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return []string{"."}, nil
}

return nil, fmt.Errorf("failed to read buildpack.yml: %w", err)
}

var config struct {
Go struct {
Targets []string `yaml:"targets"`
} `yaml:"go"`
}

err = yaml.NewDecoder(file).Decode(&config)
if err != nil {
return nil, fmt.Errorf("failed to decode buildpack.yml: %w", err)
}

if len(config.Go.Targets) == 0 {
return []string{"."}, nil
}

for index, target := range config.Go.Targets {
if strings.HasPrefix(target, string(filepath.Separator)) {
return nil, fmt.Errorf("failed to determine build targets: %q is an absolute path, targets must be relative to the source directory", target)
}

config.Go.Targets[index] = fmt.Sprintf("./%s", filepath.Clean(target))
}

return config.Go.Targets, nil
}
Loading

0 comments on commit 867d713

Please sign in to comment.