GithubHelp home page GithubHelp logo

gitpod-io / leeway Goto Github PK

View Code? Open in Web Editor NEW
152.0 12.0 19.0 14.82 MB

Leeway is a heavily caching build system for Go, Typescript and Docker projects.

License: MIT License

Go 99.99% Shell 0.01%

leeway's Introduction

Leeway

Leeway is a heavily caching build system for Go, Yarn and Docker projects. Its features are:

  • source dependent versions: leeway computes the version of a package based on the sources, dependencies and configuration that make up this package. There's no need (or means) to manually version packages.
  • two-level package cache: leeway caches its build results locally and remotely. The remote cache (a Google Cloud Storage bucket) means builds can share their results and thus become drastically faster.
  • parallel builds: because leeway understands the dependencies of your packages it can build them as parallel as possible.
  • built-in support for Yarn and Go: leeway knows how to link, build and test Yarn and Go packages and applications. This makes building software written in those languages straight forward.
  • build arguments: leeway supports build arguments which can parametrize packages at build time. We support version dependent arguments (where the version depends on the argument value), component-wide constants and workspace-level defaults.
  • rich CLI: leeways CLI supports deep inspection of the workspace and its structure. Its output is easy to understand and looks good.

Leeway structures a repository in three levels:

  • The workspace is the root of all operations. All component names are relative to this path. No relevant file must be placed outside the workspace. The workspace root is marked with a WORKSPACE.yaml file.
  • A components is single piece of standalone software. Every folder in the workspace which contains a BUILD.yaml file is a component. Components are identifed by their path relative to the workspace root.
  • Packages are the buildable unit in leeway. Every component can define multiple packages in its build file. Packages are identified by their name prefixed with the component name, e.g. some-component:pkg.

Installation

Leeway assumes its running on Linux or macOS. It is very very unlikely that this runs on Windows out-of-the-box. To install, just download and unpack a release.

Build setup

Workspace

Place a file named WORKSPACE.yaml in the root of your workspace. For convenience sake you should set the LEEWAY_WORKSPACE_ROOT env var to the path of that workspace. For example:

touch WORKSPACE.yaml
export LEEWAY_WORKSPACE_ROOT=$PWD

The WORKSPACE.yaml may contain some default settings for the workspace:

# defaultTarget is package we build when just running `leeway build`
defaultTarget: some/package:name
#defaultArgs are key=value pairs setting default values for build arguments
defaultArgs:
  key: value

Users can override, and provide additional default arguments using a WORKSPACE.args.yaml file in the workspace root. This is useful for providing local overrides which you might not want to commit to Git. The WORKSPACE.args.yaml takes key value pairs which become available as build arguments. The values herein take precedence over the default arguments in the WORKSPACE.yaml.

foo: bar
key: value

Component

Place a BUILD.yaml in a folder somewhere in the workspace to make that folder a component. A BUILD.yaml primarily contains the packages of that components, but can also contain constant values (think of them as metadata). For example:

# const defines component-wide constants which can be used much like build arguments. Only string keys and values are supported.
const:
  internalName: example
  someRandomProperty: value
packages:
- ...
scripts:
- ...

Script

Scripts are a great way to automate tasks during development time (think yarn scripts). Unlike packages they do not run in isolation by default, but have access to the original workspace. What makes scripts special is that they can dependent on packages which become available to a script in the PATH and as environment variables.

Under the scripts key in the component's BUILD.yaml add:

# name is the component-wide unique name of script. Packages and scripts do NOT share a namespace.
# You can have a package called foo and a script called foo within the same component.
name: some-script-name
# description provides a short synopsis of the script. Shown when running `leeway collect scripts`.
description: A sentence describing what the script is good for.
# Deps list dependencies to packages (NOT scripts) which must be built prior to running this script.
# All built dependencies get added to the PATH environment variable. This is handy if your workspace
# contains tools you want to use in a script.
deps:
- some/other:package
# Env sets environment variables which are present during script execution.
env:
- MESSAGE=hello
# Workdir changes the workdir location/layout of working dir of the script. The following choices are available:
# - origin (default): execute the script in the directory of the containing component in the original workspace.
#                     This is the default mode and handy if one wants to automate tasks in the development workspace.
# - packages:         produces a filesystem layout much like during a generic package build where all deps are
#                     found by their name in a temporary directory. This provides some isolation from the original
#                     workspace, while giving full access to the built dependencies.
workdir: origin
# The actual script. For now, only bash scripts are supported. The shebang is added automatically.
script: |
  echo $MESSAGE, this is where the script goes
  if [ "A$(ps -o comm= -p $$)" = "Abash" ]; then
    echo "it's the bash alright"
  fi
  echo "build args work to: ${myBuildArg}"

Package

A package is an entry in a BUILD.yaml in the packages section. All packages share the following fields:

# name is the component-wide unique name of this package
name: must-not-contain-spaces
# Package type must be one of: go, yarn, docker, generic
type: generic
# Sources list all sources of this package. Entries can be double-star globs and are relative to the component root.
# Avoid listing sources outside the component folder.
srcs:
- "**/*.yaml"
- "glob/**/path"
# Deps list dependencies to other packages which must be built prior to building this package. How these dependencies are made
# available during build depends on the package type.
deps:
- some/other:package
# Argdeps makes build arguments version relevant. I.e. if the value of a build arg listed here changes, so does the package version.
argdeps:
- someBuildArg
# Env is a list of key=value pair environment variables available during package build
env:
- CGO_ENABLED=0
# Config configures the package build depending on the package type. See below for details
config:
  ...

Go packages

config:
  # Packaging method. See https://godoc.org/github.com/gitpod-io/leeway/pkg/leeway#GoPackaging for details. Defaults to library.
  packaging: library
  # If true leeway runs `go generate -v ./...` prior to testing/building. Defaults to false.
  generate: false
  # If true disables `go test -v ./...`
  dontTest: false
  # If true disables the enforcement of `go fmt`. By default, if the code is not gofmt'ed the build fails.
  dontCheckGoFmt: false
  # If true disables the linting stage.
  dontLint: false
  # Overrides the `go build .` command. Supersedes buildFlags.
  buildCommand: []
  # [DEPRECATED: use buildCommand instead] A list of flags passed to `go build`. Useful for passing `ldflags`.
  buildFlags: []
  # Command that's executed to lint the code
  lintCommand: ["golangci-lint", "run"]
  # GoMod can point to a go.mod file outside the component root. Leeway expects a go.sum alongside the go.mod.
  goMod: "../go.mod"

Yarn packages

config:
  # yarnlock is the path to the yarn.lock used to build this package. Defaults to `yarn.lock`. Useful when building packages in a Yarn workspace setup.
  # Automatically added to the package sources.
  yarnlock: "yarn.lock"
  # tsconfig is the path to the tsconfig.json used to build this package. Detauls to `tsconfig.json`
  # Automatically added to the package sources.
  tsconfig: "tsconfig.json"
  # packaging method. See https://godoc.org/github.com/gitpod/leeway/pkg/leeway#YarnPackaging for details.
  # Defaults to library
  packaging: library
  # If true disables `yarn test`
  dontTest: false
  # commands overrides the default commands executed during build
  commands:
    install: ["yarn", "install"]
    build: ["yarn", "build"]
    test: ["yarn", "test"]

Docker packages

config:
  # Dockerfile is the name of the Dockerfile to build. Automatically added to the package sources.
  dockerfile: "Dockerfile"
  # Metadata produces a metadata.yaml file in the resulting package tarball.
  metadata:
    foo: bar
  # build args are Docker build arguments. Often we just pass leeway build arguments along here.
  buildArgs:
  - arg=value
  - other=${someBuildArg}
  # image lists the Docker tags leeway will use and push to
  image:
  - gitpod/leeway:latest
  - gitpod/leeway:${__pkg_version}

The first image name of each Docker dependency which pushed an image will result in a build argument. This mechanism enables a package to build the base image for another one, by using the build argument as FROM value. The name of this build argument is the package name of the dependency, transformed as follows:

  • / is replaced with _
  • : is replaced with __
  • all uppercase.

E.g. component/nested:docker becomes COMPONENT_NESTED__DOCKER.

Generic packages

config:
  # A list of commands to execute. Beware that the commands are not executed in a shell. If you need shell features (e.g. wildcards or pipes),
  # wrap your command in `sh -c`. Generic packages without commands result in an empty tar file.
  commands:
  - ["echo", "hello world"]
  - ["sh", "-c", "ls *"]

Dynaimc package scripts

Packages can be dynamically produced within a component using a dynamic package script named BUILD.js. This ECMAScript 5.1 file is executed using Goja and produces a packages array which contains the package struct much like they'd exist within the BUILD.yaml. For example:

Leeway interacts with the script using global variables, specifically:

  • args [input] a JavaScript object containing the build arguments which have explicitely been passed to leeway.
  • packages [output] where the script produces an array of package structures akin to those found in a BUILD.yaml file.

BUILD.js file

let packages = [];

let deps = [];
for(let i = 0; i < 5; i++) {
  const name = "hello-"+i;
  deps.push(name);
  packages.push({
    name: name,
    type: "generic",
    config: {
      commands: [
        ["echo", args.msg + ": hello from "+i]
      ]
    }
  });
}

packages.push({
  name: "all",
  type: "generic",
  deps: deps.map(d => ":" + d),
})

Equivalent BUILD.yaml

pacakages:
- name: all
  type: generic
  deps:
    - hello-1
    - hello-2
    - hello-3
    - hello-4
    - hello-5
- name: hello-1
  type: generic
  config:
    commands:
      - ["echo", "${msg}: hello from 1"]
- name: hello-2
  type: generic
  config:
    commands:
      - ["echo", "${msg}: hello from 2"]
- name: hello-3
  type: generic
  config:
    commands:
      - ["echo", "${msg}: hello from 3"]
...

Note that for a BUILD.js to become effective/be recodnized there needs to a (possibly empty) BUILD.yaml in the same directory.

Build arguments

In a package definition one can use build arguments. Build args have the form of ${argumentName} and are string-replaced when the package is loaded. It's advisable to use build args only within the config section of packages. Constants and built-in build args do not even work outside of the config section.

Leeway supports built-in build arguments:

  • __pkg_version resolves to the leeway version hash of a component.
  • __git_commit contains the current Git commit if the build is executed from within a Git working copy. If this variable is used and the build is not executed from within a Git working copy the variable resolution will fail. If the package sources contain uncommitted files/directories, then __pkg_version will be appended to __git_commit
  • __git_commit_short shortened version of __git_commit to the first 7 characters.

Package Variants

Leeway supports build-time variance through "package variants". Those variants are defined on the workspace level and can modify the list of sources, environment variables and config of packages. For example consider a WORKSPACE.YAML with this variants section:

variants:
- name: nogo
  srcs:
    exclude:
    - "**/*.go"
  config:
    go:
      buildFlags:
        - tags: foo

This workspace has a (nonsensical) nogo variant that, when enabled, excludes all go source files from all packages. It also changes the config of all Go packages to include the -tags foo flag. You can explore the effects of a variant using collect and describe, e.g. leeway --variant nogo collect files vs leeway collect files. You can list all variants in a workspace using leeway collect variants.

Environment Manifest

Leeway does not control the environment in which it builds the packages, but assumes that all required tools are available already (e.g. go or yarn). This however can lead to subtle failure modes where a package built in one enviroment ends up being used in another, because no matter of the environment they were built in, they get the same version.

To prevent such issues, leeway computes an environment manifest which contains the versions of the tools used, as well as some platform information. The entries in that manifest depend on the package types used by that workspace, e.g. if only Go packages exist in the workspace, only go version, GOOS and GOARCH will be part of the manifest. You can inspect a workspace's environment manifest using leeway describe environment-manifest.

You can add your own entries to a workspace's environment manifest in the WORKSPACE.yaml like so:

environmentManifest:
  - name: gcc
    command: ["gcc", "--version"]

Using this mechanism you can also overwrite the default manifest entries, e.g. "go" or "yarn".

Configuration

Leeway is configured exclusively through the WORKSPACE.yaml/BUILD.yaml files and environment variables. The following environment variables have an effect on leeway:

  • LEEWAY_WORKSPACE_ROOT: Contains the path where to look for a WORKSPACE file. Can also be set using --workspace.
  • LEEWAY_REMOTE_CACHE_STORAGE: Defines the remote caching storage provider. Valid values are "GCP" and "AWS". Defaults to "GCP".
  • LEEWAY_REMOTE_CACHE_BUCKET: Enables remote caching using GCP or S3 buckets. Required credentials depend on the storage provider:
  • LEEWAY_CACHE_DIR: Location of the local build cache. The directory does not have to exist yet.
  • LEEWAY_BUILD_DIR: Working location of leeway (i.e. where the actual builds happen). This location will see heavy I/O which makes it advisable to place this on a fast SSD or in RAM.
  • LEEWAY_YARN_MUTEX: Configures the mutex flag leeway will pass to yarn. Defaults to "network". See https://yarnpkg.com/lang/en/docs/cli/#toc-concurrency-and-mutex for possible values.
  • LEEWAY_EXPERIMENTAL: Enables exprimental features

Provenance (SLSA) - EXPERIMENTAL

leeway can produce provenance information as part of a build. At the moment only SLSA is supported. This supoprt is experimental.

Provenance generation is enabled in the WORKSPACE.YAML file.

provenance:
  enabled: true
  slsa: true

Once enabled, all packages carry an attestation bundle which is compliant to the SLSA v0.2 spec in their cached archive. The bundle is complete, i.e. not only contains the attestation for the package build, but also those of its dependencies.

Dirty vs clean Git working copy

When building from a clean Git working copy, leeway will use a reference to the Git remote origin as material (part of the SLSA link).

Signing attestations

To support SLSA level 2, leeway can sign the attestations it produces. To this end, you can provide the filepath to a key either as part of the WORKSPACE.yaml or through the LEEWAY_PROVENANCE_KEYPATH environment variable.

Inspecting provenance

You can inspect the generated attestation bundle by extracting it from the built and cached archive. For example:

# run a build
leeway build //:app

# export the attestation bundle
leeway provenance export //:app

# export the decoded attestation bundle
leeway provenance export --decode //:app

# verify that all material came from a Git repo
leeway provenance assert --git-only //:app

# verify that all subjects were built using leeway
leeway provenance asert --built-with-leeway //:app

# decode an attestation bundle from a file (also works for assertions)
leeway provenance export --decode file://some-bundle.jsonl

Caveats

  • provenance is part of the leeway package version, i.e. when you enable provenance that will naturally invalidate previously built packages.
  • if attestation bundle entries grow too large this can break the build process. Use LEEWAY_MAX_PROVENANCE_BUNDLE_SIZE to set the buffer size in bytes. This defaults to 2MiB. The larger this buffer is, the larger bundle entries can be used, but the more memory the build process will consume. If you exceed the default, inspect the bundles first (especially the one that fails to load) and see if the produced subjects make sense.

Debugging

When a build fails, or to get an idea of how leeway assembles dependencies, run your build with leeway build -c local (local cache only) and inspect your $LEEWAY_BUILD_DIR.

CLI tips

How can I build a package in the current component/folder?

leeway build .:package-name

Is there bash autocompletion?

Yes, run . <(leeway bash-completion) to enable it. If you place this line in .bashrc you'll have autocompletion every time.

How can I find all packages in a workspace?

# list all packages in the workspace
leeway collect
# list all package names using Go templates
leeway collect -t '{{ range $n := . }}{{ $n.Metadata.FullName }}{{"\n"}}{{end}}'
# list all package names using jq
leeway collect -o json | jq -r '.[].metadata.name'

How can I find out more about a package?

# print package description on the console
leeway describe some/components:package
# dump package description as json
leeway describe some/components:package -o json

How can I inspect a packages depdencies?

# print the dependency tree on the console
leeway describe dependencies some/components:package
# print the denendency graph as Graphviz dot
leeway describe dependencies --dot some/components:package
# serve an interactive version of the dependency graph
leeway describe dependencies --serve=:8080 some/components:package

How can I print a component constant?

# print all constants of the component in the current working directory
leeway describe const .
# print all constants of a component
leeway describe const some/component/name
# print the value of the `someName` constant of `some/component/name`
leeway describe const some/component/name -o json | jq -r '.[] | select(.name=="foo").value'

How can I find all components with a particular constant?

leeway collect components -l someConstant

How can I export only a workspace the way leeway sees it, i.e. based on the packages?

LEEWAY_EXPERIMENTAL=true leeway export --strict /some/destination

macOS: leeway fails with "cp --parents" not being a valid command

The way depends on GNU utilities. Install them and make sure they're in your path.

brew install coreutils

Contributing

Creating a new release

Releases of Leeway are created by the release workflow which uses goreleaser/goreleaser-action.

To create a new release create a new Git tag and push it:

git tag vX.X.X
git push origin vX.X.X

This will trigger the workflow which you can find here. Once it's done a new release will show up in GitHub here.

leeway's People

Contributors

adrienthebo avatar aledbf avatar ashutoshpw avatar corneliusludmann avatar csweichel avatar dependabot[bot] avatar fntlnz avatar geropl avatar ghuntley avatar iqqbot avatar jenting avatar mads-hartmann avatar meysholdt avatar nandajavarma avatar utam0k avatar vulkoingim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

leeway's Issues

Does not run on macOS

README says "Leeway assumes its running on Linux or macOS"

While it might assume that, it doesn't actually run on OS X since it uses "cp --parents" which is not supported on OS X - it fails with "illegal option" errors i.e. a BSD/GNU issue.

As no-one has reported this for the >2yrs that option has been in the code base the simplest option may be to remove that statement from the README and say it only runs on Linux?

While it might be possible to suggest people install the GNU fileutils this has other potential drawbacks.

Alternatively, Swift/T hit a similar issue some time ago, resulting in this workaround so something similar could be added, but probably not worth the effort in maintaining parity.

Thoughts?

Stack overflow when using `remote-push` cache level

fatal error: stack overflow

runtime stack:
runtime.throw(0x8e93c1, 0xe)
	/home/gitpod/go/src/runtime/panic.go:774 +0x72
runtime.newstack()
	/home/gitpod/go/src/runtime/stack.go:1046 +0x6e9
runtime.morestack()
	/home/gitpod/go/src/runtime/asm_amd64.s:449 +0x8f

goroutine 1 [running]:
github.com/typefox/leeway/cmd.(*pushOnlyRemoteCache).Upload(0xc000395670, 0x988bc0, 0xc000395690, 0xc0002b8000, 0x1f, 0x1f, 0x0, 0x0)
	/workspace/leeway/cmd/build.go:189 +0x86 fp=0xc0207de338 sp=0xc0207de330 pc=0x7f1af6
github.com/typefox/leeway/cmd.(*pushOnlyRemoteCache).Upload(0xc000395670, 0x988bc0, 0xc000395690, 0xc0002b8000, 0x1f, 0x1f, 0x0, 0x0)
	/workspace/leeway/cmd/build.go:190 +0x5d fp=0xc0207de388 sp=0xc0207de338 pc=0x7f1acd
github.com/typefox/leeway/cmd.(*pushOnlyRemoteCache).Upload(0xc000395670, 0x988bc0, 0xc000395690, 0xc0002b8000, 0x1f, 0x1f, 0x0, 0x0)
	/workspace/leeway/cmd/build.go:190 +0x5d fp=0xc0207de3d8 sp=0xc0207de388 pc=0x7f1acd
github.com/typefox/leeway/cmd.(*pushOnlyRemoteCache).Upload(0xc000395670, 0x988bc0, 0xc000395690, 0xc0002b8000, 0x1f, 0x1f, 0x0, 0x0)
	/workspace/leeway/cmd/build.go:190 +0x5d fp=0xc0207de428 sp=0xc0207de3d8 pc=0x7f1acd
github.com/typefox/leeway/cmd.(*pushOnlyRemoteCache).Upload(0xc000395670, 0x988bc0, 0xc000395690, 0xc0002b8000, 0x1f, 0x1f, 0x0, 0x0)
	/workspace/leeway/cmd/build.go:190 +0x5d fp=0xc0207de478 sp=0xc0207de428 pc=0x7f1acd
github.com/typefox/leeway/cmd.(*pushOnlyRemoteCache).Upload(0xc000395670, 0x988bc0, 0xc000395690, 0xc0002b8000, 0x1f, 0x1f, 0x0, 0x0)

v0.0.7

improved component variant management

In order to add a new JB IDE we have to add 4 components [1][2] now in similar as we do it for other IDEs. Whenever we change the process we need to update all these components. It would be nice if we could have an ability to specify only one component like a template and then instantiation of this template with different args. Or something like that.

Add werft job

Now that leeway lives in the gitpod-io org, we should add a werft job that builds leeway.
Ideally we'd add one that builds the release as well.

Improve testability

We'd like to establish a pattern that we like for writing tests for Leeway.

Leeway is heavily dependent on having a filesystem to operate on. We were thinking of including a small abstraction for specifying Leeway projects (workspace, components, packages, scripts) in Go code which we could then use to generate proper Leeway workspaces in a temporary folder. We could then run leeway there, and write assertions against the Go model.

It would be nice to do this at the same time as #122 so that we can be a bit test-driven in that refactor of Leeway.

Document ephemeral packages

The README currently doesn't mention ephemeral packages packages. We should extend the README to include details about

  1. What are ephemeral packages and when would you want to use them
  2. How do ephemeral packages behave

Extend `--dont-test` to "generic" packages

Currently in Gitpod, we have some packages that are of type generic but are actually tests packages. In some scenarios one would like to skip those tests a) to reduce turn-arounds or b) those tests contain assumptions about their environment which one has to re-create to run them (like we have in our CI).

leeway supports a --dont-test flag that allows to skip go test and yarn test execution for Go and Yarn package types, respectively. I see two options to extend that flag to also cover packages of type generic:
a) have a test command in the package configuration which is not executed when --dont-test is passed in (or it is not specified); analogous to the (implicit) test commands in Go and Yarn packages - except that it has no implicit default value
b) inject a environment variable that generic builds can check

Personally I'm in favor of A.

@csweichel WDYT?

Update: When having a closer look at the code I noticed there is some variance regarding the command(s) configuration:

Go

command string, assumes go test just works

Yarn

commands: { install, build, test string }

Generic

commands: [][]string

Parallelize leeway vet

leeway vey is currently not parallelised which makes it quite slow - too sow to run as a pre-commit hook. In a gitpod-io/gitpod workspace time leeway vet currently takes about 9s

Let us parallelize it and see how fast we can get it ๐ŸŽ

Packages build because they're deps of a script don't get uploaded to the remote cache

Bug description

deps packages that are being built as part of leeway run don't get uploaded to the remote cachce

Steps to reproduce

Modify previewctl so that you get a new hash

printf "\n// $(date)\n" >> /workspace/gitpod/dev/preview/previewctl/main.go

Run dev/preview/previewctl:install, remove the caches, and run dev/preview/previewctl:install again.

leeway run dev/preview/previewctl:install
rm -rf /tmp/build /tmp/cache
leeway run dev/preview/previewctl:install

Expected behavior

I would have expected the 2nd invocation to download the dev/preview/previewctl:cli package from the remote cache.

It does re-use the cache if it exists. E.g. if you use leeway build, then delete the local caches, and then leeway run it does indeed use the remote cache.

rm -rf /tmp/build /tmp/cache
leeway build dev/preview/previewctl:cli
rm -rf /tmp/build /tmp/cache
leeway run dev/preview/previewctl:install

Example repository

No response

Anything else?

No response

Early upload successfully built artifacts to remote cache

Bug description

Leeway, when building the 'installer:docker' target in a gitpod.io workspace has a lot of build dependencies..
The default concurrency limit is 16, so it starts building many pkgs in parallel..

In my case, there are always some temporary build errors with some pkgs that stops the entire build process.
The key problem is that Leeway only starts uploading build artifacts once at the end of a successful build (or missing to do so due to premature exit on failures ?):
โ˜๏ธ uploading build artifacts to remote cache

Since due to the build failure, many packages that are previously built are not present in the remote (and local) cache, so when I restart the build, I see many of those are getting re-built from scratch again and again. This is causing almost an endless loop, if the temporary errors keep popping up & I keep re-running the build command.

The only workaround I found was to specify lighter build targets from the list of components that are not present in the remote cache.. let them finish and see they are getting uploaded.. I can typically get the whole build done in like 10-15 total attempts, which is super annoying & time consuming.

Can this be improved, like by triggering both an image push & remote cache artifact upload at the end of each successfully built component, when it prints package build succeded (has a typo)?

Another idea for improvement: actually, uploading the artifacts to the remote cache at the very end of the build process is fine, but Leeway should wait for all forked background / parallel builds' results before exiting and still upload the successfully built targets' artifacts to remote cache in case of an overall build failure.. So let's say the entire build includes 25 targets, there is a build error with a single target (may fail the dependent ones), I would expect Leeway to print a stat in the end of how many of the 25 were built successfully, explicitly mentioning the failed targets, as well as that it uploads the successful ones to the remote cache, so on next attempt those pkgs will not get rebuilt.

Steps to reproduce

In description. I can provide build logs, if needed. Just let me know.

Expected behavior

No response

Example repository

No response

Anything else?

No response

'leeway build' fails to run `yarn` scripts

Bug description

leeway build fails to run yarn scripts under some circumstances.

Despite a dev dependency being declared in the package's package.json, leeway build is unable to find an executable installed to the package's node_modules.

Steps to reproduce

In the example repository there is one workspace with one package, components/foo.

  • Running yarn test directly in the components/foo directly works.
  • Running either leeway build .:lib from components/foo or leeway build components/foo:lib from the root fails with:
๐Ÿ”ง  build  components/foo:lib  (version 92b68613c489efca7d1d2195bdb6456f5eace828)

[components/foo:lib] build started (version 92b68613c489efca7d1d2195bdb6456f5eace828)
[components/foo:lib] installing
[components/foo:lib] building
[components/foo:lib] yarn run v1.22.19
[components/foo:lib] $ mocha './*.spec.js' --exclude './node_modules/**'
[components/foo:lib] /bin/sh: 1: mocha: not found
[components/foo:lib] error Command failed with exit code 127.
[components/foo:lib] info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
[components/foo:lib] package build failed
[components/foo:lib] Reason: exit status 127
build failed
Reason: build failed
FATA[0000] build failed

leeway build is unable to find mocha despite it being defined as a dev dependency of the package.

The issue is not mocha specific; changing the BUILD.yaml test command:

https://github.com/andrew-farries/leeway-playground/blob/fa9a052b9e0ff3886154ceef2db17ffe320fc45b/components/foo/BUILD.yaml#L14

to test: ["yarn", "cowsay"] and then running leeway build as before also fails with the same error about being unable to find the cowsay executable despite it having a script and a dev dependency in the package.json.

Expected behavior

leeway build should successfully run yarn test.

Example repository

https://github.com/andrew-farries/leeway-playground

Anything else?

This issue also affects Gitpod, with some packages being unable to run yarn tests.

The additional context relevant to Gitpod is in this internal Slack thread.

Decide on future of graphview

We have to decide if we want to improve the graphview capabilities of Leeway, or throw it away:

TODO: How do I use it now? I tried the following in gitpod-io/gitpod

leeway describe dependencies --serve :8081

But just get error when visit https://8081-gitpodio-gitpod-6qjxlthvsu7.ws-eu77.gitpod.io/web/dist/

Screenshot 2022-11-21 at 15 20 02

yarn cache being re-used between multiple runs results in wrong package used

Bug description

When building ws-manager-bridge I noticed that leeway did not seem to pick up changes from it's direct dependency gitpod-protocol. Checking with leeway describe turned out that hashes would be calculated directly.

@csweichel hinted at rm -rf /tmp/build/yarn-cache/, which fixed the issue: as yarn only caches by version, it delivers old versions for instead of changes ones.

Possible dupe: #71

Steps to reproduce

  • leeway build components/ws-manager-bridge:docker
  • add function in components/gitpod-protocol/src/ and use that in ws-manager-bridge
  • leeway build components/ws-manager-bridge:docker => note how that fails

Expected behavior

No response

Example repository

No response

Anything else?

No response

Extend `vet` so that it produces a warning if any .gitignore files are in the srcs input to a package.

Is your feature request related to a problem? Please describe

  1. It's normal to add build artefacts to .gitignore
  2. Leeways' srcs globs makes it easy to include unintended files

Describe the behaviour you'd like

I'd like for vet to provide a warning if any files that are .gitignored are part of the expanded srcs.

Describe alternatives you've considered

Today you can probably manually check for this using a combination of leeway describe, jq and a bit of bash.

Additional context

See gitpod-io/gitpod#14157

Add linting support to Go builds

We should have linting support in Go builds. The linter would work in analogy to Go tests, i.e. they're run by default and can be disabled with dontLint. By default we'd call golangci-lint, but the command can be overridden in the package config, in analogy to how the test command can be overriden.

Readme: Short section about (pre-)release process

I just came into the situation where I wanted to have a pre-release for a branch and was not sure how to do so. I guess I could twiddle with goreleaser on my branch so it builds + releases it. A few lines on this would be awesome!

[scripts] Exit code is not passed on

If a script exits with a non-zero exit code, leeway does not pass this exit code on.

E.g.

scripts:
  - name: demo
    script: exit -1

will not cause leeway to exit with -1

Leeway vet doesn't return a non-zero exit code on failures

Currently leeway vet doesn't return a non-zero exit code when it fails.

From a gitpod-io/gitpod workspace running 0.5.2-d3ff827:

$ leeway vet
ERRO[0008] components/ide/jetbrains/image: EOF          
$ echo $?
0

Here's a screenshot of one of our builds where you see the same behaviour in Werft:

Screenshot 2022-11-21 at 14 55 23

Keep in mind that this specific error will go away with this PR

Allow running multiple Leeway scripts in parallel

We're currently using Leeway scripts to create preview environments in gitpod-io/gitpod. Currently we have the following sequential invocations of leeway run. This is leaving some parallelism on the table.

leeway run dev/preview:create-preview
leeway run dev/preview:build
previewctl install-context
leeway run dev/preview:deploy-gitpod
leeway run dev/preview:deploy-monitoring-satellite

Ideally create-preview and build would run in parallel. Then we'd wait for install-context, and then run deploy-gitpod and deploy-monitoring-satellite in parallel.

I did a quick prototype to add parallelism through the use of ephemeral packages and dependencies between them. It works. But controlling parallelism this way in YAML really isn't nice. I find it hard to read and it takes a while to parse where the "forks and joins" are happening. Even if we allowed scripts to have dependencies on scripts, we'd still have to express the parallel execution in YAML.

I'd prefer to extend leeway such that you can pass multiple scripts to leeway run. Our implementation would then be:

leeway run dev/preview:create-preview dev/preview:build
previewctl install-context
leeway run dev/preview:deploy-gitpod dev/preview:parallel-deploy-all

[feature-request] docker: "remote-tag" images instead of pull+tag+push in case of cache-hits

When the cache registry already contains the docker image that we want to build (cache hit) leeway currently does basically a docker pull <version-tag> && docker tag <version-tag> <new-tag> && docker push <new-tag>. This seems to spam IO on our build nodes and takes time as we do a "full" build on each branch creation/push.

It would be nice if we could just create a new manifest which contains the already existing layers instead ("remote tag").

Support more remote cache destinations

From what I've gathered in

leeway/cmd/root.go

Lines 177 to 189 in 1d0912e

case "GCP":
return leeway.GSUtilRemoteCache{
BucketName: remoteCacheBucket,
}
case "MINIO":
return leeway.MinioRemoteCache{
BucketName: remoteCacheBucket,
}
default:
return leeway.GSUtilRemoteCache{
BucketName: remoteCacheBucket,
}
}

there are only two cache storage providers: GCS and MINIO. I would love to see the adoption of more services, but I understand the potential hassle and the size of this request. With that said, I think something that could help is https://github.com/rclone/rclone. It supports like, everything out there and could be used to store the remote cache on not just standard services like Azure Blob Storage or AWS S3, but also places which everyone has access too, like Google Drive, OneDrive and others.

In addition to all of these, rclone also supports custom FTP and HTTP remotes, which some folks would for sure appreciate being options. I've been using rclone for about 2 years now and had an awesome experience with it, it's very easy to deal with.

Drawbacks

The biggest con of implementing rclone specifically is the fact it is a CLI, not a library, which could limit us in some ways (such as error reporting and checking).

command which prints build directory

I would like to know where next build will stored, it would be nice to have a command which prints it, i.e.

leeway build dir "myapp:mypackage"
/tmp/build/myapp.mypackage.abcd

prefixwriter panics occasionally

every now and then a build fails with

panic: runtime error: slice bounds out of range [240:33]

goroutine 459 [running]:
github.com/segmentio/textio.(*PrefixWriter).Buffered(...)
	/go/pkg/mod/github.com/segmentio/[email protected]/prefix.go:38
github.com/segmentio/textio.(*PrefixWriter).Write.func1(0xc00055a000, 0x21, 0x8000, 0xc000565d0a)
	/go/pkg/mod/github.com/segmentio/[email protected]/prefix.go:53 +0x2c1
github.com/segmentio/textio.forEachLine(0xc00055a000, 0x21, 0x8000, 0xc000565d60)
	/go/pkg/mod/github.com/segmentio/[email protected]/prefix.go:148 +0xcb
github.com/segmentio/textio.(*PrefixWriter).Write(0xc00013a050, 0xc00055a000, 0x21, 0x8000, 0xc0000aa728, 0xc0002b0001, 0x1c)
	/go/pkg/mod/github.com/segmentio/[email protected]/prefix.go:47 +0xb7
github.com/typefox/leeway/pkg/leeway.(*ConsoleReporter).PackageBuildLog(0xc000406060, 0xc000107e00, 0x0, 0xc00055a000, 0x21, 0x8000)
	/github/workspace/pkg/leeway/reporter.go:126 +0x1bd
github.com/typefox/leeway/pkg/leeway.(*reporterStream).Write(0xc00000c080, 0xc00055a000, 0x21, 0x8000, 0x21, 0x0, 0x0)
	/github/workspace/pkg/leeway/build.go:911 +0x62
io.copyBuffer(0x6d8940, 0xc00000c080, 0x6d8aa0, 0xc000486010, 0xc00055a000, 0x8000, 0x8000, 0x404ba5, 0xc0003b3c80, 0xc0002227b0)
	/usr/local/go/src/io/io.go:404 +0x1fb
io.Copy(...)
	/usr/local/go/src/io/io.go:364
os/exec.(*Cmd).writerDescriptor.func1(0xc0003b3c80, 0xc0002227b0)
	/usr/local/go/src/os/exec/exec.go:311 +0x63
os/exec.(*Cmd).Start.func1(0xc0003a4000, 0xc00000c120)
	/usr/local/go/src/os/exec/exec.go:435 +0x27
created by os/exec.(*Cmd).Start
	/usr/local/go/src/os/exec/exec.go:434 +0x608

presence of dynamic packages leads to linking error during `leeway exec`

Bug description

leeway exec cannot find the dynamic package install/preview:docker (when it is building the dependency tree?) and fails with:

gitpod /workspace/gitpod/components/server (gpl/12758-list-plans) $ yarn watch
yarn run v1.22.19
$ leeway exec --package .:app --transitive-dependencies --filter-type yarn --components --parallel -- yarn build -w --preserveWatchOutput
FATA[0000] cannot load workspace                         error="linking error in package components:all: package \"install/preview:docker\" is unknown"
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Steps to reproduce

Expected behavior

It should execute yarn builds instead

Example repository

No response

Anything else?

No response

Capture environment details in the package version

Leeway assumes an ever fixed, consistent environment in which the packages are built. E.g. if one builds a package with Go 1.13 it will have the same version as if it were built with Go 1.14, when in fact the build-results are clearly different.

This issue arrises from leeway's source-driven versioning where the build tools are not part of the of package manifest.

Leeway should capture parts of the environment (e.g. Go version, Node version, yarn version, operating system) and make those environmental details part of the package version. Ideally the "environment manifest" is configurable on a workspace level, where outer workspaces override/enhance the "environment manifest template" of inner workspaces. That WORKSPACE.yaml config could look like:

environmentManifest:
  - name: go
    command: ["go", "version"]

Leeway would build the environment manifest prior to computing a package's version. By default leeway would capture the go, node, yarn and docker version, as well as the GOOS and GOARCH values.

Failed to build gitpod component typescript-grpc lib using leeway

os: Ubuntu (18.04.2 LTS (Bionic Beaver))
node version: v12.16.2
gitpod version: 0.6.0 self-hosted
leeway version: 0.2.0-fe5c3b2

I tried to build lib component typescript-grpc using this command dozens of times on the root directory of Gitpod project.
export LEEWAY_WORKSPACE_ROOT =$PWD leeway build components/supervisor-api/typescript-grpc:lib -v
Sadly, the failure result showed up every time I tried. The detailed message of building was:
[components/supervisor-api/typescript-grpc:lib] error /tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/grpc-tools: Command failed. [components/supervisor-api/typescript-grpc:lib] Exit code: 1 [components/supervisor-api/typescript-grpc:lib] Command: node-pre-gyp install [components/supervisor-api/typescript-grpc:lib] Arguments: [components/supervisor-api/typescript-grpc:lib] Directory: /tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/grpc-tools [components/supervisor-api/typescript-grpc:lib] Output: [components/supervisor-api/typescript-grpc:lib] node-pre-gyp info it worked if it ends with ok [components/supervisor-api/typescript-grpc:lib] node-pre-gyp info using [email protected] [components/supervisor-api/typescript-grpc:lib] node-pre-gyp info using [email protected] | linux | x64 [components/supervisor-api/typescript-grpc:lib] node-pre-gyp WARN Using needle for node-pre-gyp https download [components/supervisor-api/typescript-grpc:lib] node-pre-gyp info check checked for "/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/grpc-tools/bin/grpc_tools.node" (not found) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp http GET https://node-precompiled-binaries.grpc.io/grpc-tools/v1.9.1/linux-x64.tar.gz [components/supervisor-api/typescript-grpc:lib] node-pre-gyp http 200 https://node-precompiled-binaries.grpc.io/grpc-tools/v1.9.1/linux-x64.tar.gz [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! install error [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack Error: There was a fatal problem while downloading/extracting the tarball [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at afterExtract (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/node-pre-gyp/lib/install.js:100:33) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.afterTarball (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/node-pre-gyp/lib/install.js:176:9) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.emit (events.js:310:20) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.[maybeClose] (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/tar/lib/unpack.js:171:12) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.opt.ondone (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/tar/lib/unpack.js:89:23) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.emit (events.js:310:20) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.[emit] (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/tar/lib/parse.js:235:12) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.[maybeEnd] (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/tar/lib/parse.js:351:17) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.[consumeChunk] (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/tar/lib/parse.js:359:21) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! stack at Unpack.write (/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/tar/lib/parse.js:315:25) [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! System Linux 4.4.0-18362-Microsoft [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! command "/usr/local/bin/node" "/tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/grpc-tools/node_modules/.bin/node-pre-gyp" "install" [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! cwd /tmp/build/components-supervisor-api-typescript-grpc--lib.7d464a8081d27f349c10d918ccb3b9844ea4ce5c/node_modules/grpc-tools [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! node -v v12.16.2 [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! node-pre-gyp -v v0.15.0 [components/supervisor-api/typescript-grpc:lib] node-pre-gyp ERR! not ok [components/supervisor-api/typescript-grpc:lib] There was a fatal problem while downloading/extracting the tarball
It seems that something wrong occurred during the command node-pre-gyp install was running.
So I tried to get into the module grcp-tools, and ran that command manually. The result was successful. So I don't know why it can't succeed using leeway build.
Could you please help me find out what happens?

'leeway vat --ingore-warning' returns false positives

Example: https://werft.gitpod-dev.com/job/gitpod-build-gpl-test.1/logs#build:build
(the output at the top is from an ignored leeway vat --ingore-warning.).

One of the error messages:
time="2021-01-21T09:36:09Z" level=error msg="gitpod-core/components/ws-daemon-api/go:lib: Go package does not have go package config"
The build later runs fine, though.

To reproduce: run leeway vet --ignore-warnings in the gitpod-io/gitpod repo.

The check: 42ce465#diff-018cf017b8fa7d438ced439fa3d022e22d062478f60f46cad88ff79d3217149bR53-R56
The commit that introduced it: 42ce465

Support "resolutions" in typescript libraries

Currently only the local package.json is copied into the build directory. Because the workspace global package.json (with it's resolutions fields) is missing package resolution behaves different to outside of leeway.

One way to "fix" this would be to add a config workspaceRootPackageJson and merge that into the local package.json.

/cc @akosyakov

Provide tab-completion for `leeway run`

Is your feature request related to a problem? Please describe

Currently tab-completion for Leeway doesn't work for "scripts", so leeway run <tab> will just give you normal shell tab-completion.

Describe the behaviour you'd like

leeway run <tab> should suggest all scripts in any leeway component in the workspace.

Describe alternatives you've considered

I haven't considered any alternatives.

Additional context

Nope

Builds using local build cache do not recognize changed dependencies

Bug description

We are building gitpod for arm64. We use leeway. Whenever we change things in dependency components (like gitpod-db:lib or gitpod-protocol:lib), leeway builds the changes in those dependency components, but then gives the depender components the old built version from the cache instead of the newly built version.

Steps to reproduce

Set LEEWAY_BUILD_DIR and LEEWAY_CACHE_DIR to local paths, and disable remote cache.
Using gitpod-io/gitpod repo:
leeway build component/server:app

This should build.
Now, go into components/gitpod-db and change the name of any function relied on by server. E.g. search and replace "findRunningInstance(" with "findRunningInstanceX(" in gitpod/components/gitpod-db/src/workspace-db.ts

Now run leeway build component/server:app again.
Leeway notes that gitpod-db needs to rebuild, and rebuilds it. However, when it builds server:app, it builds it with old version of gitpod-db

Expected behavior

No response

Example repository

No response

Anything else?

I don't think this is an arm issue. We get it on x86 builds too

Output download log only when using --debug command

Ref: gitpod-io/gitpod#14439
Leeway outputs a ton of log when it downloads packages, and my assumption is that in 99% cases that is quite unnecessary bit of information, and it makes it harder to get the point which actually matters (like build failure error).

I propose to hide that log output and only show it if leeway is running with some sort of --debug switch enabled.

[feature-request] Allow to pass multiple (read-only) caches

This would allow to make use of already built packages in environments where one build (B) "extends" the other (A), but is build in an different environment. A is always build first (and thus it's cache populated), so the subsequent build of B should be able to benefit from that.

Retire ephemeral packages in favour of scripts

We are considering removing support for ephemeral packages in favour of scripts.

At the moment packages can't have dependencies on scripts, so we'd have to extend Leeway to allow that.

We should ensure that any script a package depends on has the workdir setting set to packages to ensure they don't modify anything in the source tree (intentional or not)

More context in this internal meeting note.

Two packages in the same component with the same source set result in hash conflict

We don't take each individual package config when building the manifest but the BUILD.yaml of the parent component. Thus if in the same component there are packages that have the same source set they get the same manifest/hash.

One way to solve this could be to add the package name to the manifest, better yet add the raw package config instead of the BUILD.yaml.

sha256Hash fails to hash symlink to directory

Bug description

When building the image for JetBrains Rider, we hit this error:

 build  components/ide/jetbrains/image:download-rider-latest  (version 249f098fb2645cf5fadbd511dcee0ec35561bb05)

[components/ide/jetbrains/image:download-rider-latest] build started (version 249f098fb2645cf5fadbd511dcee0ec35561bb05)
[components/ide/jetbrains/image:download-rider-latest] package build failed
[components/ide/jetbrains/image:download-rider-latest] Reason: cannot compute hash of /backend/bin/JBDevice.framework/Versions/Current: read /tmp/build/components-ide-jetbrains-image--download-rider-latest.249f098fb2645cf5fadbd511dcee0ec35561bb05/backend/bin/JBDevice.framework/Versions/Current: is a directory
:cloud:  uploading build artifacts to remote cache
build failed
Reason: build failed
FATA[0041] build failed  

In the JetBrains tar.gz there are a couple of files which are symlink other directories. Example:

  • JBDevice.framework/Versions/Current
  • Bridge.framework/Versions/Current

Those are the ones causing the problem

Related code in Leeway: https://github.com/gitpod-io/leeway/blob/main/pkg/leeway/provenance.go#L332
Related PR in gitpod: gitpod-io/gitpod#14524

Steps to reproduce

  1. Start a workspace for this PR: gitpod-io/gitpod#14524
  2. Edit components/ide/jetbrains/image/download.sh and comment out the rm commands
  3. Run leeway build components/ide/jetbrains/image:download-rider-latest

Expected behavior

No response

Example repository

No response

Anything else?

No response

golangci-lint is not part of the environment manifest

Bug description

We currently have a build failure because the version of golangci-lint is not part of the environment manifest. I.e. golangci-lint changed but we did not rebuild. This causes things to break now.

Steps to reproduce

leeway build install/installer:raw-app on gitpod-io/gitpod@main

Expected behavior

No response

Example repository

No response

Anything else?

No response

Cannot parse Git status when files are renamed

Bug description

When files have been renamed, leeway fails to parse the Git status and assumes all files are "dirty":

WARN[0000] cannot parse git status: assuming all files are dirty  error="cannot parse git status \"R  bootstrap/infra/README.md -> bootstrap/README.md\": expected two segments, got 4"

Steps to reproduce

Rename a file in the Git working copy using git mv

Expected behavior

No response

Example repository

No response

Anything else?

No response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.