Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: better caching support #951

Merged
merged 14 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/dependencies.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/goreleaser/goreleaser@latest
github.com/mgechev/revive@latest
github.com/securego/gosec/v2/cmd/gosec@latest
honnef.co/go/tools/cmd/staticcheck@2023.1
78 changes: 46 additions & 32 deletions .github/workflows/pr_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,37 @@ jobs:
uses: actions/setup-go@v4
with:
go-version: 1.20.x
# NOTE: Manage GitHub Actions cache https://github.com/fastly/cli/actions/caches
# NOTE: Manage GitHub Actions cache via https://github.com/fastly/cli/actions/caches
# This is useful if you need to clear the cache when a dependency doesn't update correctly.
- name: "Restore golang bin cache"
id: go-bin-cache
#
# REFERENCES:
# https://www.airplane.dev/blog/caching-golang-tests-in-ci
# https://markphelps.me/posts/speed-up-your-go-builds-with-actions-cache/
#
- id: go-cache-paths
name: Retrieve Go Paths
run: |
echo "gobin=$(go env GOPATH)/bin" >> $GITHUB_OUTPUT # speed up dependency installs
echo "gobuild=$(go env GOCACHE)" >> $GITHUB_OUTPUT # speed up `go test` runs
echo "gomod=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT # speed up use of third-party modules
- name: Go Bin Cache
id: go-bin-deps
uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.gobin }}
key: ${{ runner.os }}-lint-go-bin-${{ hashFiles('.github/dependencies.txt') }}
- name: Go Build Cache
uses: actions/cache@v3
with:
path: ~/go/bin
key: ${{ runner.os }}-go-bin-${{ hashFiles('~/go/bin') }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-bin-
- name: "Restore golang mod cache"
path: ${{ steps.go-cache-paths.outputs.gobuild }}
key: ${{ runner.os }}-lint-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-mod-
path: ${{ steps.go-cache-paths.outputs.gomod }}
key: ${{ runner.os }}-lint-go-mod-${{ hashFiles('**/go.sum') }}
- name: "Install dependencies"
if: steps.go-bin-cache.outputs.cache-hit != 'true'
if: steps.go-bin-deps.outputs.cache-hit != 'true'
run: make dependencies
shell: bash
- name: "Run go mod tidy"
Expand All @@ -73,7 +83,7 @@ jobs:
run: make gosec
shell: bash
test:
needs: config
needs: [config]
strategy:
matrix:
tinygo-version: [0.27.0]
Expand All @@ -91,22 +101,22 @@ jobs:
- uses: Integralist/setup-tinygo@v1.0.0
with:
tinygo-version: ${{ matrix.tinygo-version }}
- name: "Restore golang bin cache"
- name: Retrieve Go Paths
id: go-cache-paths
shell: bash # IMPORTANT: without this Windows OS will not work.
run: |
echo "gobuild=$(go env GOCACHE)" >> $GITHUB_OUTPUT # speed up `go test` runs
echo "gomod=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT # speed up use of third-party modules
- name: Go Build Cache
uses: actions/cache@v3
with:
path: ~/go/bin
key: ${{ runner.os }}-go-bin-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-bin-
- name: "Restore golang mod cache"
path: ${{ steps.go-cache-paths.outputs.gobuild }}
key: ${{ runner.os }}-test-go-build-${{ hashFiles('**/go.sum') }}
- name: Go Mod Cache
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-mod-
path: ${{ steps.go-cache-paths.outputs.gomod }}
key: ${{ runner.os }}-test-go-mod-${{ hashFiles('**/go.sum') }}
- name: "Install Rust"
uses: dtolnay/rust-toolchain@stable
- name: "Add wasm32-wasi Rust target"
Expand All @@ -124,10 +134,14 @@ jobs:
name: config-artifact-${{ github.sha }}
- name: "Move Config"
run: mv config.toml pkg/config/config.toml
# NOTE: Windows should fail quietly for 'test' pre-requisite target.
# On Windows, executing `make config` works fine.
# But via GitHub Actions the ../../scripts/config.sh isn't run.
# This is because you can't nest PowerShell instances.
- name: "Modify git cloned repo files 'modified' times"
run: go run ./scripts/go-test-cache/main.go
# NOTE: Windows should fail quietly running pre-requisite target of `test`.
#
# On Windows, executing `make config` directly works fine.
# But when `config` is a pre-requisite to running `test`, it fails.
# But only when run via GitHub Actions.
# The ../../scripts/config.sh isn't run because you can't nest PowerShell instances.
# Each GitHub Action 'run' step is a PowerShell instance.
# And each instance is run as: powershell.exe -command ". '...'"
- name: "Test suite"
Expand Down
13 changes: 8 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,14 @@ all: config dependencies tidy fmt vet staticcheck gosec semgrep test build insta
# Update CI tools used by ./.github/workflows/pr_test.yml
.PHONY: dependencies
dependencies:
$(GO_BIN) install github.com/securego/gosec/v2/cmd/gosec@latest
$(GO_BIN) install honnef.co/go/tools/cmd/staticcheck@2023.1
$(GO_BIN) install github.com/mgechev/revive@latest
$(GO_BIN) install github.com/goreleaser/goreleaser@latest
if [[ "$$(uname)" == 'Darwin' ]]; then brew install semgrep; fi
@while read -r line || [ -n "$$line" ]; do \
$(GO_BIN) install $$line; \
done < .github/dependencies.txt
@if [ "$$(uname)" = 'Darwin' ]; then \
if ! command -v semgrep &> /dev/null; then \
brew install semgrep; \
fi \
fi

# Clean up Go modules file.
.PHONY: tidy
Expand Down
67 changes: 34 additions & 33 deletions pkg/commands/logging/common/flags.go
Original file line number Diff line number Diff line change
@@ -1,86 +1,87 @@
package common

import (
"github.com/fastly/cli/pkg/cmd"
"github.com/fastly/kingpin"

"github.com/fastly/cli/pkg/cmd"
)

// AccountName defines the account-name flag.
func AccountName(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("account-name", "The google account name used to obtain temporary credentials (default none)").Action(c.Set).StringVar(&c.Value)
func AccountName(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("account-name", "The google account name used to obtain temporary credentials (default none)").Action(c.Set).StringVar(&c.Value)
}

// Format defines the format flag
func Format(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("format", "Apache style log formatting. Your log must produce valid JSON").Action(c.Set).StringVar(&c.Value)
func Format(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("format", "Apache style log formatting. Your log must produce valid JSON").Action(c.Set).StringVar(&c.Value)
}

// GzipLevel defines the gzip flag
func GzipLevel(cmd *kingpin.CmdClause, c *cmd.OptionalInt) {
cmd.Flag("gzip-level", "What level of GZIP encoding to have when dumping logs (default 0, no compression)").Action(c.Set).IntVar(&c.Value)
func GzipLevel(command *kingpin.CmdClause, c *cmd.OptionalInt) {
command.Flag("gzip-level", "What level of GZIP encoding to have when dumping logs (default 0, no compression)").Action(c.Set).IntVar(&c.Value)
}

// Path defines the path flag
func Path(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("path", "The path to upload logs to").Action(c.Set).StringVar(&c.Value)
func Path(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("path", "The path to upload logs to").Action(c.Set).StringVar(&c.Value)
}

// MessageType defines the path flag
func MessageType(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("message-type", "How the message should be formatted. One of: classic (default), loggly, logplex or blank").Action(c.Set).StringVar(&c.Value)
func MessageType(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("message-type", "How the message should be formatted. One of: classic (default), loggly, logplex or blank").Action(c.Set).StringVar(&c.Value)
}

// Period defines the period flag
func Period(cmd *kingpin.CmdClause, c *cmd.OptionalInt) {
cmd.Flag("period", "How frequently log files are finalized so they can be available for reading (in seconds, default 3600)").Action(c.Set).IntVar(&c.Value)
func Period(command *kingpin.CmdClause, c *cmd.OptionalInt) {
command.Flag("period", "How frequently log files are finalized so they can be available for reading (in seconds, default 3600)").Action(c.Set).IntVar(&c.Value)
}

// FormatVersion defines the format-version flag
func FormatVersion(cmd *kingpin.CmdClause, c *cmd.OptionalInt) {
cmd.Flag("format-version", "The version of the custom logging format used for the configured endpoint. Can be either 2 (the default, version 2 log format) or 1 (the version 1 log format). The logging call gets placed by default in vcl_log if format_version is set to 2 and in vcl_deliver if format_version is set to 1").Action(c.Set).IntVar(&c.Value)
func FormatVersion(command *kingpin.CmdClause, c *cmd.OptionalInt) {
command.Flag("format-version", "The version of the custom logging format used for the configured endpoint. Can be either 2 (the default, version 2 log format) or 1 (the version 1 log format). The logging call gets placed by default in vcl_log if format_version is set to 2 and in vcl_deliver if format_version is set to 1").Action(c.Set).IntVar(&c.Value)
}

// CompressionCodec defines the compression-codec flag
func CompressionCodec(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("compression-codec", `The codec used for compression of your logs. Valid values are zstd, snappy, and gzip. If the specified codec is "gzip", gzip_level will default to 3. To specify a different level, leave compression_codec blank and explicitly set the level using gzip_level. Specifying both compression_codec and gzip_level in the same API request will result in an error.`).Action(c.Set).StringVar(&c.Value)
func CompressionCodec(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("compression-codec", `The codec used for compression of your logs. Valid values are zstd, snappy, and gzip. If the specified codec is "gzip", gzip_level will default to 3. To specify a different level, leave compression_codec blank and explicitly set the level using gzip_level. Specifying both compression_codec and gzip_level in the same API request will result in an error.`).Action(c.Set).StringVar(&c.Value)
}

// Placement defines the placement flag
func Placement(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("placement", "Where in the generated VCL the logging call should be placed, overriding any format_version default. Can be none or waf_debug. This field is not required and has no default value").Action(c.Set).StringVar(&c.Value)
func Placement(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("placement", "Where in the generated VCL the logging call should be placed, overriding any format_version default. Can be none or waf_debug. This field is not required and has no default value").Action(c.Set).StringVar(&c.Value)
}

// ResponseCondition defines the response-condition flag
func ResponseCondition(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("response-condition", "The name of an existing condition in the configured endpoint, or leave blank to always execute").Action(c.Set).StringVar(&c.Value)
func ResponseCondition(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("response-condition", "The name of an existing condition in the configured endpoint, or leave blank to always execute").Action(c.Set).StringVar(&c.Value)
}

// TimestampFormat defines the timestamp-format flag
func TimestampFormat(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("timestamp-format", `strftime specified timestamp formatting (default "%Y-%m-%dT%H:%M:%S.000")`).Action(c.Set).StringVar(&c.Value)
func TimestampFormat(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("timestamp-format", `strftime specified timestamp formatting (default "%Y-%m-%dT%H:%M:%S.000")`).Action(c.Set).StringVar(&c.Value)
}

// PublicKey defines the public-key flag
func PublicKey(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("public-key", "A PGP public key that Fastly will use to encrypt your log files before writing them to disk").Action(c.Set).StringVar(&c.Value)
func PublicKey(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("public-key", "A PGP public key that Fastly will use to encrypt your log files before writing them to disk").Action(c.Set).StringVar(&c.Value)
}

// TLSCACert defines the tls-ca-cert flag
func TLSCACert(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("tls-ca-cert", "A secure certificate to authenticate the server with. Must be in PEM format").Action(c.Set).StringVar(&c.Value)
func TLSCACert(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("tls-ca-cert", "A secure certificate to authenticate the server with. Must be in PEM format").Action(c.Set).StringVar(&c.Value)
}

// TLSHostname defines the tls-hostname flag
func TLSHostname(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("tls-hostname", "Used during the TLS handshake to validate the certificate").Action(c.Set).StringVar(&c.Value)
func TLSHostname(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("tls-hostname", "Used during the TLS handshake to validate the certificate").Action(c.Set).StringVar(&c.Value)
}

// TLSClientCert defines the tls-client-cert flag
func TLSClientCert(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("tls-client-cert", "The client certificate used to make authenticated requests. Must be in PEM format").Action(c.Set).StringVar(&c.Value)
func TLSClientCert(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("tls-client-cert", "The client certificate used to make authenticated requests. Must be in PEM format").Action(c.Set).StringVar(&c.Value)
}

// TLSClientKey defines the tls-client-key flag
func TLSClientKey(cmd *kingpin.CmdClause, c *cmd.OptionalString) {
cmd.Flag("tls-client-key", "The client private key used to make authenticated requests. Must be in PEM format").Action(c.Set).StringVar(&c.Value)
func TLSClientKey(command *kingpin.CmdClause, c *cmd.OptionalString) {
command.Flag("tls-client-key", "The client private key used to make authenticated requests. Must be in PEM format").Action(c.Set).StringVar(&c.Value)
}
14 changes: 8 additions & 6 deletions pkg/commands/logging/s3/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"fmt"
"io"

"github.com/fastly/go-fastly/v8/fastly"

"github.com/fastly/cli/pkg/cmd"
"github.com/fastly/cli/pkg/commands/logging/common"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/global"
"github.com/fastly/cli/pkg/manifest"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v8/fastly"
)

// CreateCommand calls the Fastly API to create an Amazon S3 logging endpoint.
Expand Down Expand Up @@ -126,14 +127,15 @@ func (c *CreateCommand) ConstructInput(serviceID string, serviceVersion int) (*f
// SecretKey or the IAMRole is required, but they are mutually
// exclusive. The kingpin library lacks a way to express this constraint
// via the flag specification API so we enforce it manually here.
if !c.AccessKey.WasSet && !c.SecretKey.WasSet && !c.IAMRole.WasSet {
switch {
case !c.AccessKey.WasSet && !c.SecretKey.WasSet && !c.IAMRole.WasSet:
return nil, fmt.Errorf("error parsing arguments: the --access-key and --secret-key flags or the --iam-role flag must be provided")
} else if (c.AccessKey.WasSet || c.SecretKey.WasSet) && c.IAMRole.WasSet {
case (c.AccessKey.WasSet || c.SecretKey.WasSet) && c.IAMRole.WasSet:
// Enforce mutual exclusion
return nil, fmt.Errorf("error parsing arguments: the --access-key and --secret-key flags are mutually exclusive with the --iam-role flag")
} else if c.AccessKey.WasSet && !c.SecretKey.WasSet {
case c.AccessKey.WasSet && !c.SecretKey.WasSet:
return nil, fmt.Errorf("error parsing arguments: required flag --secret-key not provided")
} else if !c.AccessKey.WasSet && c.SecretKey.WasSet {
case !c.AccessKey.WasSet && c.SecretKey.WasSet:
return nil, fmt.Errorf("error parsing arguments: required flag --access-key not provided")
}

Expand Down Expand Up @@ -250,7 +252,7 @@ func ValidateRedundancy(val string) (redundancy fastly.S3Redundancy, err error)
default:
err = fmt.Errorf("unknown redundancy: " + val)
}
return
return redundancy, err
}

// Exec invokes the application logic for the command.
Expand Down
15 changes: 2 additions & 13 deletions pkg/manifest/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,7 @@ func TestManifestPrepend(t *testing.T) {
}

func TestDataServiceID(t *testing.T) {
sid := os.Getenv(env.ServiceID)
defer func(sid string) {
os.Setenv(env.ServiceID, sid)
}(sid)

err := os.Setenv(env.ServiceID, "001")
if err != nil {
t.Fatal(err)
}
t.Setenv(env.ServiceID, "001")

// SourceFlag
d := manifest.Data{
Expand All @@ -232,10 +224,7 @@ func TestDataServiceID(t *testing.T) {
}

// SourceFile
err = os.Setenv(env.ServiceID, "")
if err != nil {
t.Fatal(err)
}
t.Setenv(env.ServiceID, "")
_, src = d.ServiceID()
if src != manifest.SourceFile {
t.Fatal("expected SourceFile")
Expand Down
2 changes: 1 addition & 1 deletion pkg/text/lines.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

// Lines is the struct that is used by PrintLines
type Lines map[string]interface{}
type Lines map[string]any

// PrintLines pretty prints a Lines struct with one item per line.
// The map is sorted before printing and a newline is added at the beginning
Expand Down
5 changes: 3 additions & 2 deletions pkg/text/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
"strings"
"syscall"

"github.com/fastly/cli/pkg/sync"
"github.com/mitchellh/go-wordwrap"
"golang.org/x/term"

"github.com/fastly/cli/pkg/sync"
)

// DefaultTextWidth is the width that should be passed to Wrap for most
Expand Down Expand Up @@ -111,7 +112,7 @@ func IsStdin(r io.Reader) bool {
// IsTTY returns true if fd is a terminal. When used in combination
// with IsStdin, it can be used to determine whether standard input
// is being piped data (i.e. IsStdin == true && IsTTY == false).
// Provide STDOUT as a way to determine whether formating and/or
// Provide STDOUT as a way to determine whether formatting and/or
// prompting is acceptable output.
func IsTTY(fd any) bool {
if s, ok := fd.(*sync.Writer); ok {
Expand Down
3 changes: 3 additions & 0 deletions scripts/go-test-cache/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module go-test-cache

go 1.20
Loading