Giter VIP home page Giter VIP logo

tools's Introduction

tools's People

Contributors

acj avatar andig avatar anupcshan avatar bboehmke avatar bradfitz avatar damdo avatar darkskiez avatar dependabot[bot] avatar doridian avatar dtchanpura avatar janisstreib avatar joneskoo avatar khirbat avatar markdrayton avatar mikioh avatar nhanb avatar oliverpool avatar patagonicus avatar stapelberg avatar twpayne avatar willglynn 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

Watchers

 avatar  avatar  avatar  avatar

tools's Issues

Use of go install interacts badly with $GOBIN

This issue might be related to trying to use gokrazy from macOS. For sure, building gokrazy images on non-Linux systems is not yet supported and this is likely one issue that will need to fixed to enable that.

On my macOS system, I have:

export GOPATH=$HOME
export GOBIN=$GOPATH/bin

When I run

gokr-packer -overwrite=/dev/disk3 github.com/gokrazy/hello

I get the error:

2017/03/23 19:42:55 packer.go:222: installing [github.com/gokrazy/hello]
go install: cannot install cross-compiled binaries when GOBIN is set
go install: cannot install cross-compiled binaries when GOBIN is set
go install: cannot install cross-compiled binaries when GOBIN is set

I suspect that this is related to the use of go install at gotool.go:71. I suspect that the cross-compiled binaries should be built with go build but rather than installing them, the built binaries should be copied from their output location (wherever that is) to their final location.

go build has an -o option to specify the path of the output binary, but this obviously only works when building one binary at a time and is incompatible with gokrazy's use of ./... to build all binaries in a tree.

exit status 1

Exits with exit status 1 without any clue of what might be wrong:

$ gokr-packer -overwrite_boot=/tmp/boot.fat -overwrite_root=/tmp/root.fat github.com/gokrazy/hello
2020/06/20 09:23:36 packer.go:345: installing [github.com/gokrazy/hello]
2020/06/20 09:23:36 gotool.go:68: getting incomplete packages [github.com/gokrazy/gokrazy/cmd/dhcp github.com/gokrazy/gokrazy/cmd/ntp github.com/gokrazy/hello github.com/gokrazy/gokrazy github.com/gokrazy/kernel github.com/gokrazy/firmware]
2020/06/20 09:26:00 write.go:149: writing boot file system
2020/06/20 09:26:00 packer.go:793: exit status 1

My go environment is:

$ go version
go version go1.14.2 linux/amd64

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/rgl/.cache/go-build"
GOENV="/home/rgl/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/rgl/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/rgl/Applications/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/rgl/Applications/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build478674939=/tmp/go-build -gno-record-gcc-switches"

Relative replace directives stopped working

I'm using replace directives with relative paths for some instance-specific modules, e.g.

replace maxte/gokrazy/gpio-counter => ../../../../gpio-counter

Since 626d0c7 this fails when running gok update with

[...]

Including loadable kernel modules from:
/home/max/go/pkg/mod/github.com/gokrazy/[email protected]/lib/modules
2023/02/25 16:02:09 lstat ../../../../gpio-counter: no such file or directory

gokr-packer fails when default instance (~/gokrazy/hello) exists

When gokr-packer invokes the packing logic it also calls GenerateSBOM() here.

In turn within GenerateSBOM to set the buildDir we use the instancePath obtained via config.InstancePath().

Which for gokr-packer, where no instancePath is set, it falls back to ~/gokrazy/hello.

This in turn breaks gokr-packer if ~/gokrazy/hello exists:

2023/03/12 17:34:43 open /Users/ddonati/gokrazy/hello/builddir/github.com/gokrazy/gokrazy/go.mod: no such file or directory

To reproduce:

# create the default "hello" instance at the default gokrazy instance path
gok -i hello new

# then use gokr-packer to build/overwrite something to a full file.

Add an alternative way to specify build enviroment variables

Currently build variables like GOARM, GOOS and GOARCH are simply forwarded from the current environment with some default values:

func goEnv() []string {
goarch := "arm64" // Raspberry Pi 3
if e := os.Getenv("GOARCH"); e != "" {
goarch = e
}
env := os.Environ()
for idx, e := range env {
if strings.HasPrefix(e, "CGO_ENABLED=") {
env[idx] = "CGO_ENABLED=0"
}
}
return append(env, fmt.Sprintf("GOARCH=%s", goarch))
}

However this does not play well with go run:

GOARCH=arm go run github.com/gokrazy/tools/cmd/gokr-packer
    github.com/gokrazy/hello

Since GOARCH will be used by go run, the resulting gokr-packer executable will not run on and amd64 machine for instance.


I would like to be able to use go run github.com/gokrazy/tools/cmd/gokr-packer, so that the gokr-packer version can be managed by go.mod using a tools.go file:

//go:build tools

package tools

import (
	_ "github.com/gokrazy/tools/cmd/gokr-packer"
)

Here are the possible solutions to this problem, that I thought of:

  • support prefixed environment variables: GOKR_GOARCH= go run ... for instance (which would override `GOARCH)
  • add a flag to support build environment file: go run .../gokr-packer -buildenv=env with env being a file like:
GOARCH=arm

What do you think would be the most elegant way to fix this issue ?
I can then try to craft a PR.

Using `replace` to test a locally compiled kernel triggers `CheckDir(...): ... malformed file path ...`

I am compiling https://github.com/gokrazy-community/kernel-rpi-os-32 locally.
When I adjust the go.mod file to use it:

replace github.com/gokrazy-community/kernel-rpi-os-32 => /home/path/to/kernel-rpi-os-32

I get an error:

Including loadable kernel modules from:
/home/path/to/kernel-rpi-os-32/dist/lib/modules
2024/07/02 23:34:23 CheckDir(/home/path/to/kernel-rpi-os-32): /home/path/to/kernel-rpi-os-32/linux-sources/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c: malformed file path "linux-sources/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c": "aux" disallowed as path element component on Windows

This is due to:

h, err := hashDir(dir)

which calls hashDir, which calls zip.CheckDir, which is unhappy.

Ideally, only the dist subdir should be checked (since this is the actually imported path).

TLS applied when -insecure set

Using the updater to generate certs with a new hostname works fine and uses http:

❯ go run github.com/gokrazy/tools/cmd/gokr-packer -update yes -hostname foo -tls=self-signed -insecure github.com/gokrazy/breakglass github.com/gokrazy/hello
2021/01/04 12:33:12 packer.go:353: building [github.com/gokrazy/breakglass github.com/gokrazy/hello]
Generating new self-signed certificate...
https://gokrazy:FIat2WTD8i3tJikkvIJI@foo/
2021/01/04 12:33:19 httpclient.go:46: Using certificate /Users/andig/Library/Application Support/gokrazy/hosts/foo/cert.pem
2021/01/04 12:33:19 packer.go:827: probing url for https: Get "http://foo": dial tcp: lookup foo: no such host
exit status 1

Doing the same with a previously generated host that contains certificates in the config folder still tries to use TLS although -insecure is given:

❯ go run github.com/gokrazy/tools/cmd/gokr-packer -update yes -hostname evcc -tls=self-signed -insecure github.com/gokrazy/breakglass github.com/gokrazy/hello
2021/01/04 12:33:36 packer.go:353: building [github.com/gokrazy/breakglass github.com/gokrazy/hello]
https://gokrazy:FIat2WTD8i3tJikkvIJI@evcc/
2021/01/04 12:33:41 httpclient.go:46: Using certificate /Users/andig/Library/Application Support/gokrazy/hosts/evcc/cert.pem
2021/01/04 12:33:41 packer.go:827: checking target partuuid support: Get "https://gokrazy:***@evcc/update/features": dial tcp 192.168.0.63:443: connect: connection refused
exit status 1

Seems the problem is https://github.com/gokrazy/tools/blob/master/cmd/gokr-packer/packer.go#L451 where https is prematurely applied.

Will send PR.

gokr-packer: Support go generate?

In cases where a Go program needs to do some pre-build setup before gokr-packer installs it. The scope of this feature could be limited with --tags gokrazy so it only runs generate commands specifically intended for gokrazy.

My use case is to build/update binaries needed for wifi support (gokrazy/gokrazy#13) and pack them into Go byte slices which become available to the Go program.

I will submit a PR for this if this sounds like an OK idea.

gokrazy packer is throwing error ""

input command:

➜  hello gokr-packer \
  -update=yes \
  -overwrite=/dev/sde \
  github.com/gokrazy/hello \
  github.com/gokrazy/breakglass \
  github.com/gokrazy/serial-busybox \
  github.com/gokrazy/iptables \
  github.com/gokrazy/nsenter \
  github.com/gokrazy/podman

Expected Behavior:
write to SD card.

Current Behavior:

gokrazy packer v0.0.0-20220501111923-8579a1abae08 on GOARCH=amd64 GOOS=linux

Updating gokrazy installation on http://gokrazy

Build target: CGO_ENABLED=0 GOARCH=arm64 GOOS=linux
Build timestamp: 2022-05-04T13:07:42+05:30
Loading system CA certificates from /etc/ssl/certs/ca-certificates.crt
Building 6 Go packages:

  github.com/gokrazy/hello

  github.com/gokrazy/breakglass

  github.com/gokrazy/serial-busybox

  github.com/gokrazy/iptables
    will include extra files in the root file system
      from /home/someuser/go/pkg/mod/github.com/gokrazy/[email protected]/_gokrazy/extrafiles_arm64.tar
      last modified: 2022-04-30T00:01:36+05:30 (109h6m7s ago)

  github.com/gokrazy/nsenter

  github.com/gokrazy/podman
    will include extra files in the root file system
      from /home/someuser/go/pkg/mod/github.com/gokrazy/[email protected]/_gokrazy/extrafiles_arm64.tar
      last modified: 2021-11-15T01:13:35+05:30 (4091h54m8s ago)

[done] in 2.93s                        
2022/05/04 13:07:47 Adding loadable kernel modules from modulesDir=/home/someuser/go/pkg/mod/github.com/gokrazy/[email protected]/lib/modules
[done] in 3.28s               
2022/05/04 13:07:50 probing url for https: Get "http://gokrazy": dial tcp 192.168.1.161:80: connect: no route to host

trying to reach the gokrazy host, while i'm trying to create new image on the existing SD Card, this behavior is reproducibple when using podman package.

Relative paths cause issue in gok's --parent_dir

Yesterday I noticed that this commit 7b9dd26
slightly changed the behaviour of gok.

gok -i foo --parent_dir=./..

A relative path to the parent_dir, in fact, used to work for me, but now it doesn't seem to.
Using an absolute path as a temporary workaround fixes the issue for me.

An excerpt from the issue:

Including loadable kernel modules from:
/Users/ddonati/go/pkg/mod/github.com/gokrazy-community/[email protected]/dist/lib/modules
2023/03/12 12:17:35 Error: build directory "../gokrazy-tmp3/builddir/github.com/gokrazy/gokrazy/cmd/ntp" does not exist in "/Users/ddonati/src/private/infra/metal/gokrazy/gokrazy-tmp3/builddir/github.com/gokrazy/gokrazy/cmd/dhcp"
Try 'gok -i gokrazy-tmp3 add github.com/gokrazy/gokrazy/cmd/ntp' followed by an update.
Afterwards, your 'gok sbom' command should work: chdir ../gokrazy-tmp3/builddir/github.com/gokrazy/gokrazy/cmd/ntp: no such file or directory

Here is how to reproduce it: https://gist.github.com/damdo/0782d2c980c8e2cb14e1aee84c546aed

Segmentation violation with gok --update_all

In order to avoid a nil pointer dereference when using gok get --update_all, I needed to add

if cfg.InternalCompatibilityFlags == nil {                                                                                                                                  
  cfg.InternalCompatibilityFlags = &config.InternalCompatibilityFlags{}                                                                                                     
}                                                                                                                                                                           

before InternalCompatibilityFlags is referenced in the function getGokrazySystemPackages() in file cmd/gok/cmd/get.go. This conforms with the guards in cmd/gok/cmd/update.go and cmd/gok/cmd/overwrite.go.

extrafiles: make executable

The goal is to get grafana running on a raspberry pi 1. I struggled a lot to get it statically cross-compiled (more than for the linux kernel ;), but it now works: https://github.com/oliverpool/grafana-armv6/

I include the server binary using the special _gokrazy folder:

github.com/oliverpool/grafana-armv6/_gokrazy/extrafiles/grafana

It gets embedded in my build, but I can't run it, because it seems that the go package management removes the executable bit (the bit is set in the git repo, but missing in go/pkg/mod/github.com/oliverpool/[email protected]/_gokrazy/extrafiles/grafana).

So I currently get the error fork/exec /grafana/server: permission denied.


  1. Changing the permission before running is not possible, since the filesystem is read-only.

  2. I tried https://0xcf9.org/2021/06/22/embed-and-execute-from-memory-with-golang/ to execute it from memory, but it fails with memfd_create: function not implemented

  3. Adjusting the bit before packing is not really feasible (I don't want to mess with the go/pkg/mod folder

  4. The alternative would be to copy the executable on start to /perm/ and set the right bits, before running

This last solution should work, but does not seem clean. Do you see any better way to solve this issue?

Thank you!

mtls : mutual tls authentication

Hi, to get rid of the credential prompt and "certificate self-signed warning", I was thinking about adding a mutual tls authentication.

The device needs one (or many) cert.pem (public certificate). It will accept all clients which present a certificate signed by this certificate.
To connect to it, the client(s) will need such a certificate.

Since a self-signed certificate is already generated for https, a lazy way would be to re-use it, but to authenticate the client.


Usage examples:

Disable password-based authentication

gokr-packer -mtls -basic_auth=disabled

When -basic_auth=disabled is given, only mtls requests will be accepted. RequireAndVerifyClientCert of ClientAuthType

Self-signed certificate

gokr-packer -mtls

Implies -tls=self-signed if flag is not present.
Otherwise, use the same certificate as -tls

VerifyClientCertIfGiven of ClientAuthType

Custom certificate

gokr-packer -mtls=<path-to-cert-a.pem>,<path-to-cert-b.pem>,<path-to-cert-c.pem>

Request will need to be signed by any of these certificates or be authenticated

VerifyClientCertIfGiven of ClientAuthType


I would be willing to make a PR to implement this.

I have a working proof-of-concept with a package with embedded certificates, but before attempting to integrate it into gokrazy, I would like to know your thoughts about this idea.

undefined: unix.SYS_IOCTL on OSX

Installing latest fails on OSX 11.1:

❯ go version 
go version go1.15.6 darwin/amd64

~/htdocs/evcc-image main*
❯ go get github.com/gokrazy/tools/cmd/gokr-packer
Alias tip: gog github.com/gokrazy/tools/cmd/gokr-packer
go: found github.com/gokrazy/tools/cmd/gokr-packer in github.com/gokrazy/tools v0.0.0-20201230092410-3ba8b7f3a861
# github.com/gokrazy/tools/cmd/gokr-packer
../../go/pkg/mod/github.com/gokrazy/[email protected]/cmd/gokr-packer/parttable_darwin.go:21:33: undefined: unix.SYS_IOCTL
../../go/pkg/mod/github.com/gokrazy/[email protected]/cmd/gokr-packer/parttable_darwin.go:24:33: undefined: unix.SYS_IOCTL

I'm unsure how this happens as I have run gokr-packer before today?

Intermittent build errors: cannot find package

I've noticed gokr-packager being unable to download required packages from time to time.

Locally on OSX:

❯ gokr-packer -overwrite=image -target_storage_bytes=1153441792 github.com/gokrazy/breakglass github.com/gokrazy/hello
2021/01/09 22:58:30 packer.go:412: building [github.com/gokrazy/breakglass github.com/gokrazy/hello]
2021/01/09 22:58:33 write.go:149: writing boot file system
cannot find package "github.com/gokrazy/rpi-eeprom" in any of:
        /usr/local/Cellar/go/1.15.6/libexec/src/github.com/gokrazy/rpi-eeprom (from $GOROOT)
        /Users/andig/go/src/github.com/gokrazy/rpi-eeprom (from $GOPATH)
2021/01/09 22:58:33 packer.go:917: [go list -f {{ .Dir }} github.com/gokrazy/rpi-eeprom]: exit status 1

In Github actions (https://github.com/andig/git/runs/1674794392?check_suite_focus=true):

Run gokr-packer -overwrite=image -target_storage_bytes=1153441792 github.com/gokrazy/breakglass github.com/gokrazy/hello
2021/01/09 22:01:17 packer.go:412: building [github.com/gokrazy/breakglass github.com/gokrazy/hello]
2021/01/09 22:01:17 gotool.go:77: getting incomplete packages [github.com/gokrazy/gokrazy/cmd/dhcp github.com/gokrazy/gokrazy/cmd/ntp github.com/gokrazy/breakglass github.com/gokrazy/hello github.com/gokrazy/gokrazy github.com/gokrazy/kernel github.com/gokrazy/firmware]
2021/01/09 22:03:39 write.go:149: writing boot file system
cannot find package "github.com/gokrazy/rpi-eeprom" in any of:
    /opt/hostedtoolcache/go/1.15.6/x64/src/github.com/gokrazy/rpi-eeprom (from $GOROOT)
    /home/runner/go/src/github.com/gokrazy/rpi-eeprom (from $GOPATH)
2021/01/09 22:03:39 packer.go:917: [go list -f {{ .Dir }} github.com/gokrazy/rpi-eeprom]: exit status 1
Error: Process completed with exit code 1.

Noticed with rpi-eeprom, and kernel both. Both examples were running go1.15.6 I'd suspect some weird thing going on with go tool, maybe go proxy but have no idea where to look.

Not sure this is something for an issue in the go repo?

packer: check whether local name resolution works and print instructions if no

We could try to resolve the hostname of the local machine (taking care to avoid /etc/hosts) and/or reverse/forward resolve the default gateway’s IP address to see if local name resolution seems to work in this network.

This is motivated by a user who had only a public DNS resolver (not the local router) in their /etc/resolv.conf

DNS issue with go 1.19.3?

When gokr-packer is built with go 1.19.3, I get the following error when calling it to update an instance:

2022/11/26 10:11:52 probing url for https: Get "http://jung:1080": dial tcp: lookup jung on 192.168.1.254:53: no such host

This error occurs in internal/httpclient/httpclient.go

probeClient := &http.Client{                                                                                                                                                
  CheckRedirect: func(*http.Request, []*http.Request) error {                                                                                                               
    return http.ErrUseLastResponse // do not follow redirects                                                                                                               
  },                                                                                                                                                                        
}                                                                                                                                                                           
probeResp, err := probeClient.Get("http://" + baseUrl.Host)                                                                                                                 
if err != nil {                                                                                                                                                             
  return "", fmt.Errorf("probing url for https: %v", err)                                                                                                                   
}                      

I think this problem started after I upgraded go from 1.19.2 to 1.19.3. When I build gokr-packer with go 1.18.8 everything works as expected.

Can anyone tell if this a problem with

  • the new version of go,
  • the way the http.Client structure gets set up,
  • or the way my router or environment is set up?

Update for Go1.16

After upgrading to Go 1.16 I'm seeing this due to the switch to go modules by default:

gokr-packer -overwrite=evcc_0.46.image -target_storage_bytes=1258299392 -hostname evcc -http_port 8080 github.com/gokrazy/serial-busybox github.com/gokrazy/breakglass github.com/andig/evcc
2021/02/21 15:45:19 packer.go:539: building [github.com/gokrazy/serial-busybox github.com/gokrazy/breakglass github.com/andig/evcc]
2021/02/21 15:45:20 gotool.go:77: getting incomplete packages [github.com/gokrazy/gokrazy/cmd/dhcp github.com/gokrazy/gokrazy/cmd/ntp github.com/gokrazy/gokrazy/cmd/randomd github.com/gokrazy/serial-busybox github.com/gokrazy/breakglass github.com/gokrazy/gokrazy github.com/gokrazy/kernel github.com/gokrazy/firmware]
go: downloading github.com/gokrazy/gokrazy v0.0.0-20210130175324-e6c441f6312f
go: downloading github.com/gokrazy/kernel v0.0.0-20210216084928-394669de618f
go: downloading github.com/gokrazy/firmware v0.0.0-20210217033035-1f48ff6fd000
go get: added github.com/gokrazy/breakglass v0.0.0-20210118084620-1a4768ba6975
go get: added github.com/gokrazy/firmware v0.0.0-20210217033035-1f48ff6fd000
go get: added github.com/gokrazy/gokrazy v0.0.0-20210130175324-e6c441f6312f
go get: added github.com/gokrazy/kernel v0.0.0-20210216084928-394669de618f
go get: added github.com/gokrazy/serial-busybox v0.0.0-20200521210859-b4f0c5c3f3aa
2021/02/21 15:45:42 write.go:149: writing boot file system
no required module provides package github.com/gokrazy/rpi-eeprom; to add it:
        go get github.com/gokrazy/rpi-eeprom
2021/02/21 15:45:42 packer.go:1073: [go list -f {{ .Dir }} github.com/gokrazy/rpi-eeprom]: exit status 1
make: *** [image] Error 1

I do not have gokrazy added as a dependency to my project as it's only pulled in for certain build tags.

I can workaround this by using

GO111MODULE=auto GOOS=linux GOARCH=amd64 go get github.com/gokrazy/rpi-eeprom

but I was wondering why it's specifically complaining on the rpi-eeprom and if that dependency couldn't be added as part of tools/internal/updater?

SBOM inconsistency gok sbom vs gok overwrite/update

Turns out that #61 only fixed the SBOM inconsistencies that were caused by config.Struct pointers and by the Sudo flag, that was set here.
They did not fix the diff that is generated from the other fields in the config.InternalCompatibilityFlags, which I didn't realise, are set depending on the update mode.

  • If gok sbom is used, no config.InternalCompatibilityFlags are set.
  • If gok overwrite --full is used config.InternalCompatibilityFlags.Overwrite and config.InternalCompatibilityFlags.TargetStorageBytes are set
  • If gok overwrite --gaf is used, nothing is set
  • If gok update is used, config.InternalCompatibilityFlags.Udpdate (optionally Insecure, Testboot) is set.

These end up being accounted for when the config.Struct is stringyfied and formatted in hex, influencing the hash value for the config.json, ending up influencing the one of the final SBOM hash.

This is an issue when gok sbom is used to check for changes between the running instance (SBOM) and the current local instance folder, as it results in different SBOM hashes, which in these cases might mean an infinite loop of unnecessary updates.

Do you have any idea on how we could circumvent this issue?

I know @stapelberg mentioned that ignoring the config.InternalCompatibilityFlags that would mean same SBOMs for an instance with differing InternalCompatibilityFlags, but I would prefer this poison versus having unusable SBOMs and unnecessary updates (unless we can come up with a better solution for this :)).

WDYT?

/tmp/gokrazy-bins-... is not an ELF binary! read /tmp/gokrazy-bins-...: is a directory

After not upgrading my gokrazy for a year (very bad practice), I finally took the time to attempt an upgrade.

I stumbled upon the following error when running gok update:

(...)
[done] in 5.24s                        

2024/07/02 21:07:40 /tmp/gokrazy-bins-2652291663 is not an ELF binary! read /tmp/gokrazy-bins-2652291663: is a directory (perhaps running into https://github.com/golang/go/issues/53804?)

The mention of a directory looked highly suspicious (and unrelated to the linked issued). After some debugging (i.e. print statements and panics), it appears that the Target field is missing from the go list -json:

cmd := exec.Command("go", append([]string{"list", "-tags", "gokrazy", "-json"}, pkg)...)

In my case this likely happens because I set a custom GOBIN in the GOENV file (the env var GOVIN gets set to an empty value by Env(), but this is not enough to overwrite the value written inside my ~/.config/go/env file).

As a rough workaround, I also reset GOENV=invalid-value in Env() and it seems to be working (however it discards all my other settings, like cache location & co.)

Has anyone an idea on how to solve this in a nicer way? (if not, the empty Target field should be detected earlier and trigger an earlier failure).

Switch from bremcl/rootcerts to x/crypto/x509roots

We first need to add a Bundle() accessor to the x509roots/fallback package, then we can dump the certificates (in internal/packer/cacerts.go) like so:

	b := new(bytes.Buffer)
	for _, cert := range fallback.Bundle() {
		fmt.Fprintf(b, "# %s\n# %x\n", cert.Subject.String(), sha256.Sum256(cert.Raw))
		pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
	}

	return string(b.Bytes()), nil

Using `.` for parent_dir causes issues

When I try to use --parent_dir . things go wrong for ExtraFilePaths. At that point it no longer resolves relative paths by the instance/next to the config.json, but seems to use the build directory instead.

I suspect this is in the same ballpark as #50?

~/D/g/gotopi  (main) ❯❯❯ gok --version
https://github.com/gokrazy/tools/commit/cd4073ee8575

config.json:

{
    "Hostname": "gotosocial",
    "Packages": [
...
        "github.com/superseriousbusiness/gotosocial/cmd/gotosocial"
    ],
    "PackageConfig": {
...
        "github.com/superseriousbusiness/gotosocial/cmd/gotosocial": {
            "ExtraFilePaths": {
                "/usr/share/gotosocial/web": "assets/web"
            },
...
        }
    },
    "SerialConsole": "disabled",
    "InternalCompatibilityFlags": {}
}
Filesystem layout
~/D/g/gotopi  (main) ❯❯❯ pwd
/home/daenney/Development/github.com/gotopi

~/D/g/gotopi  (main) ❯❯❯ tree
.
├── gotosocial
│   ├── assets
│   │   ├── LICENSE
│   │   └── web
│   │       ├── assets
│   │       │   ├── default_avatars
│   │       │   │   ├── GoToSocial_icon1.png
│   │       │   │   ├── GoToSocial_icon2.png
│   │       │   │   ├── GoToSocial_icon3.png
│   │       │   │   ├── GoToSocial_icon4.png
│   │       │   │   ├── GoToSocial_icon5.png
│   │       │   │   └── GoToSocial_icon6.png
│   │       │   ├── default_header.png
│   │       │   ├── dist
│   │       │   │   ├── base.css
│   │       │   │   ├── _colors.css
│   │       │   │   ├── frontend.js
│   │       │   │   ├── index.css
│   │       │   │   ├── profile.css
│   │       │   │   ├── settings.js
│   │       │   │   ├── settings-style.css
│   │       │   │   └── status.css
│   │       │   ├── fonts
│   │       │   │   ├── noto-sans-v27-latin-700.woff
│   │       │   │   ├── noto-sans-v27-latin-700.woff2
│   │       │   │   ├── noto-sans-v27-latin-regular.woff
│   │       │   │   ├── noto-sans-v27-latin-regular.woff2
│   │       │   │   └── OFL.txt
│   │       │   ├── Fork-Awesome
│   │       │   │   ├── CONTRIBUTORS.md
│   │       │   │   ├── css
│   │       │   │   │   ├── fork-awesome.css
│   │       │   │   │   ├── fork-awesome.min.css
│   │       │   │   │   ├── fork-awesome.min.css.map
│   │       │   │   │   ├── v5-compat.css
│   │       │   │   │   ├── v5-compat.min.css
│   │       │   │   │   └── v5-compat.min.css.map
│   │       │   │   ├── fonts
│   │       │   │   │   ├── forkawesome-webfont.eot
│   │       │   │   │   ├── forkawesome-webfont.svg
│   │       │   │   │   ├── forkawesome-webfont.ttf
│   │       │   │   │   ├── forkawesome-webfont.woff
│   │       │   │   │   └── forkawesome-webfont.woff2
│   │       │   │   └── LICENSES
│   │       │   ├── LICENSE
│   │       │   ├── logo.png
│   │       │   ├── logo.svg
│   │       │   ├── mastodon.svg
│   │       │   ├── plyr.svg
│   │       │   ├── swagger.yaml
│   │       │   └── tusky.svg
│   │       └── template
│   │           ├── 404.tmpl
│   │           ├── about.tmpl
│   │           ├── authorize.tmpl
│   │           ├── confirmed.tmpl
│   │           ├── domain-blocklist.tmpl
│   │           ├── email_confirm.tmpl
│   │           ├── email_new_report.tmpl
│   │           ├── email_report_closed.tmpl
│   │           ├── email_reset.tmpl
│   │           ├── email_test.tmpl
│   │           ├── error.tmpl
│   │           ├── finalize.tmpl
│   │           ├── footer.tmpl
│   │           ├── frontend.tmpl
│   │           ├── header.tmpl
│   │           ├── index.tmpl
│   │           ├── oob.tmpl
│   │           ├── profile.tmpl
│   │           ├── sign-in.tmpl
│   │           ├── status.tmpl
│   │           └── thread.tmpl
│   ├── breakglass.authorized_keys
│   ├── builddir
│   │   ├── github.com
│   │   │   ├── gokrazy
│   │   │   │   ├── breakglass
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── fbstatus
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── firmware
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── gokrazy
│   │   │   │   │   ├── cmd
│   │   │   │   │   │   ├── dhcp
│   │   │   │   │   │   │   ├── go.mod
│   │   │   │   │   │   │   └── go.sum
│   │   │   │   │   │   ├── heartbeat
│   │   │   │   │   │   │   ├── go.mod
│   │   │   │   │   │   │   └── go.sum
│   │   │   │   │   │   ├── ntp
│   │   │   │   │   │   │   ├── go.mod
│   │   │   │   │   │   │   └── go.sum
│   │   │   │   │   │   └── randomd
│   │   │   │   │   │       ├── go.mod
│   │   │   │   │   │       └── go.sum
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── hello
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── kernel
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── rpi-eeprom
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   ├── serial-busybox
│   │   │   │   │   ├── go.mod
│   │   │   │   │   └── go.sum
│   │   │   │   └── wifi
│   │   │   │       ├── go.mod
│   │   │   │       └── go.sum
│   │   │   └── superseriousbusiness
│   │   │       └── gotosocial
│   │   │           └── cmd
│   │   │               └── gotosocial
│   │   │                   ├── go.mod
│   │   │                   └── go.sum
│   │   └── init
│   │       ├── go.mod
│   │       └── go.sum
│   └── config.json
└── README.md

34 directories, 96 files

Building with: gok --parent_dir /home/daenney/Development/github.com/gotopi -i gotosocial overwrite --root root.squashfs succeeds.

Building with gok --parent_dir . -i gotosocial overwrite --root root.squashfs fails with:

2023/06/19 13:49:53 open /home/daenney/Development/github.com/gotopi/gotosocial/builddir/github.com/superseriousbusiness/gotosocial/cmd/gotosocial/assets/web/assets/LICENSE: no such file or directory

gokr-packer: improve permission situation

One way to use the gokr-packer is to pass a filename to the -overwrite parameter and have it write an image. While this works without any additional permissions, there are two significant advantages to passing a device to -overwrite:

  1. gokr-packer can automatically determine the size of the device instead of requiring the -target_storage_bytes parameter.
  2. A lot fewer bytes have to be copied, massively speeding up the whole process.

We have a number of options as to how to obtain the required permissions:

  1. Grant capabilities which effectively mean gokr-packer is suid root (status quo): sudo setcap CAP_SYS_ADMIN,CAP_DAC_OVERRIDE=ep $(go env GOPATH)/bin/gokr-packer. This silently doesn’t work on file systems which are mounted nosuid, such as ecryptfs when using Ubuntu’s home directory encryption.
  2. Grant write permission to the target devices: sudo setfacl -m u:${USER}:rw /dev/mmc* /dev/sd*. This doesn’t stick: once you unplug the SD card, the permission change is lost.
  3. Add the user to the disk group, effectively granting write permission to all disk devices. This requires logging in again (or using newgrp) and might be too coarse-grained. Debian decided against using groups for disk access for (not concretely specified) security reasons: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=751892
  4. Have gokr-packer call sudo for the write part only. Note that calling sudo gokr-packer is not desired, as gokr-packer calls out to the go tool, which would then operate on root’s $GOPATH, not the user’s.
  5. Use udisks via dbus to format the target device. Writing the image to the target device requires authentication until storaged-project/udisks#590 is fixed. Authentication might not be available — it requires that the user is running a policykit authentication agent. Additionally, old versions of udisks are buggy in that they are unable to format the device twice in a row.
option grain sticky re-read parttable requires write needs password
setcap coarse until gokr-packer update yes suid file system no
setfacl fine until SD card re-plug no - no
disk group coarse yes no - no
sudo fine yes yes sudo by default
udisks fine yes yes recent udisks yes

panic on tar bundle in ExtraFilePaths

I'm trying to use the ExtraFilePaths to get a bunch of things bundled in a .tar.gz added to the base image. I removed the gzip compression with gzip first. Between the docs and the code I got the impression it's supposed to be used as follows:

"ExtraFilePaths": {
    "/some/directory/path": "/path/to/bundle"
}

From

func findExtraFilesInDir(pkg, dir string, fi *FileInfo) error {
ae := archiveExtraction{
dirs: make(map[string]*FileInfo),
}
ae.dirs["."] = fi // root
targetArch := packer.TargetArch()
effectivePath := dir + "_" + targetArch + ".tar"
latestModTime, err := ae.extractArchive(effectivePath)
if err != nil {
return err
}
if len(fi.Dirents) == 0 {
effectivePath = dir + ".tar"
latestModTime, err = ae.extractArchive(effectivePath)
if err != nil {
return err
}
}

I got the impression it seems I'm not supposed to append .tar to the path? When I do this it seems to ignore the file entirely. In my case I'm using a path relative to config.json, so it looks like "/usr/share/directory": "assets/tarbal-without-the-extension".

full config.json
{
    "Hostname": "gotosocial",
    "Update": {
        "HTTPPort": "8080",
        "HTTPSPort": "8443",
        "HTTPPassword": "redacted"
    },
    "Packages": [
        "github.com/gokrazy/fbstatus",
        "github.com/gokrazy/mkfs",
        "github.com/gokrazy/serial-busybox",
        "github.com/gokrazy/wifi",
        "github.com/superseriousbusiness/gotosocial/cmd/gotosocial"
    ],
    "PackageConfig": {
        "github.com/superseriousbusiness/gotosocial/cmd/gotosocial":{
            "WaitForClock": true,
            "GoBuildTags": [
                "netgo", "osusergo", "static_build", "kvformat", "notracing"
            ],
            "GoBuildFlags": [
                "-trimpath",
                "-ldflags=-s -w -extldflags '-static' -X 'main.Version=0.9.0'"
            ],
            "CommandLineFlags": [
                "server", "start", "--config=/perm/gotosocial/config.yaml"
            ],
            "ExtraFilePaths": {
                "/usr/share/gotosocial": "assets/gotosocial_0.9.0_web-assets"
            }
        }
    },
    "SerialConsole": "disabled",
    "InternalCompatibilityFlags": {}
}

But this triggers a panic:

panic: runtime error: invalid memory address or nil pointer dereference
gokrazy gok gcd4073 on GOARCH=amd64 GOOS=linux

Build target: CGO_ENABLED=0 GOARCH=arm64 GOOS=linux
Build timestamp: 2023-06-23T18:18:15+02:00
Loading system CA certificates from /etc/ssl/certs/ca-certificates.crt
Building 5 Go packages:

  github.com/gokrazy/fbstatus

  github.com/gokrazy/mkfs

  github.com/gokrazy/serial-busybox

  github.com/gokrazy/wifi

  github.com/superseriousbusiness/gotosocial/cmd/gotosocial
    will be compiled with build flags
      from /home/daenney/Development/github.com/gotosocial-deployments/raspberrypi/config.json
      last modified: 2023-06-23T18:18:13+02:00 (2s ago)
    will be compiled with build tags
      from /home/daenney/Development/github.com/gotosocial-deployments/raspberrypi/config.json
      last modified: 2023-06-23T18:18:13+02:00 (2s ago)
    will be started with command-line flags
      from /home/daenney/Development/github.com/gotosocial-deployments/raspberrypi/config.json
      last modified: 2023-06-23T18:18:13+02:00 (2s ago)
    will wait for clock synchronization before start
      from /home/daenney/Development/github.com/gotosocial-deployments/raspberrypi/config.json
      last modified: 2023-06-23T18:18:13+02:00 (2s ago)

[done] in 3.10s                        

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x48 pc=0x7fafab]

goroutine 1 [running]:
github.com/gokrazy/tools/internal/packer.(*archiveExtraction).extractArchive(0xc00017e6d8, {0xc000486210?, 0xa01c98?})
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/packer/packer.go:449 +0x42b
github.com/gokrazy/tools/internal/packer.findExtraFilesInDir({0xc00014e280, 0x39}, {0xc000148330, 0x22}, 0xc000491320)
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/packer/packer.go:487 +0x1c7
github.com/gokrazy/tools/internal/packer.FindExtraFiles(0xc00019c000)
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/packer/packer.go:554 +0xa7e
github.com/gokrazy/tools/internal/packer.(*Pack).logic(0xc00017fbf0, {0x8e1a60, 0xb})
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/packer/packer.go:1130 +0x129f
github.com/gokrazy/tools/internal/packer.(*Pack).Main(0xc00013e0f0?, {0x8e1a60?, 0x0?})
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/packer/packer.go:1875 +0x1e
github.com/gokrazy/tools/internal/gok.(*overwriteImplConfig).run(0xcb0aa0, {0x0?, 0x0?}, {0x0?, 0x0?, 0x0?}, {0x0?, 0x0?}, {0xa05fa0, 0xc000120010})
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/gok/overwrite.go:128 +0x3ed
github.com/gokrazy/tools/internal/gok.glob..func6(0xca9c20, {0xc000122900, 0x0, 0x6})
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/internal/gok/overwrite.go:39 +0x14e
github.com/spf13/cobra.(*Command).execute(0xca9c20, {0xc000124500, 0x6, 0x8})
	/home/daenney/.cache/go/pkg/mod/github.com/spf13/[email protected]/command.go:916 +0x862
github.com/spf13/cobra.(*Command).ExecuteC(0xcaa1e0)
	/home/daenney/.cache/go/pkg/mod/github.com/spf13/[email protected]/command.go:1044 +0x3bd
github.com/spf13/cobra.(*Command).Execute(0xc0000061a0?)
	/home/daenney/.cache/go/pkg/mod/github.com/spf13/[email protected]/command.go:968 +0x19
github.com/gokrazy/tools/gok.Context.Execute({{0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0, 0x0}}, {0xa0a2c8, ...})
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/gok/gok.go:34 +0xe5
main.main()
	/home/daenney/.cache/go/pkg/mod/github.com/gokrazy/[email protected]/cmd/gok/gok.go:15 +0x49

This is what the tar-archive contains:

tar -tvf assets/gotosocial_0.9.0_web-assets.tar
-rw-r--r-- root/root     34520 2023-05-24 16:53 LICENSE
-rw-r--r-- root/root     43405 2023-05-24 16:53 web/assets/Fork-Awesome/CONTRIBUTORS.md
-rw-r--r-- root/root     25608 2023-05-24 16:53 web/assets/Fork-Awesome/LICENSES
-rw-r--r-- root/root     44305 2023-05-24 16:53 web/assets/Fork-Awesome/css/fork-awesome.css
-rw-r--r-- root/root     36910 2023-05-24 16:53 web/assets/Fork-Awesome/css/fork-awesome.min.css
-rw-r--r-- root/root     15269 2023-05-24 16:53 web/assets/Fork-Awesome/css/fork-awesome.min.css.map
-rw-r--r-- root/root      8537 2023-05-24 16:53 web/assets/Fork-Awesome/css/v5-compat.css
-rw-r--r-- root/root      7422 2023-05-24 16:53 web/assets/Fork-Awesome/css/v5-compat.min.css
-rw-r--r-- root/root      2616 2023-05-24 16:53 web/assets/Fork-Awesome/css/v5-compat.min.css.map
-rw-r--r-- root/root    219194 2023-05-24 16:53 web/assets/Fork-Awesome/fonts/forkawesome-webfont.eot
-rw-r--r-- root/root    560551 2023-05-24 16:53 web/assets/Fork-Awesome/fonts/forkawesome-webfont.svg
-rw-r--r-- root/root    219004 2023-05-24 16:53 web/assets/Fork-Awesome/fonts/forkawesome-webfont.ttf
-rw-r--r-- root/root    136120 2023-05-24 16:53 web/assets/Fork-Awesome/fonts/forkawesome-webfont.woff
-rw-r--r-- root/root    109916 2023-05-24 16:53 web/assets/Fork-Awesome/fonts/forkawesome-webfont.woff2
-rw-r--r-- root/root       495 2023-05-24 16:53 web/assets/LICENSE
-rw-r--r-- root/root      8348 2023-05-24 16:53 web/assets/default_avatars/GoToSocial_icon1.png
-rw-r--r-- root/root      8815 2023-05-24 16:53 web/assets/default_avatars/GoToSocial_icon2.png
-rw-r--r-- root/root     16525 2023-05-24 16:53 web/assets/default_avatars/GoToSocial_icon3.png
-rw-r--r-- root/root     17204 2023-05-24 16:53 web/assets/default_avatars/GoToSocial_icon4.png
-rw-r--r-- root/root     16717 2023-05-24 16:53 web/assets/default_avatars/GoToSocial_icon5.png
-rw-r--r-- root/root     14846 2023-05-24 16:53 web/assets/default_avatars/GoToSocial_icon6.png
-rw-r--r-- root/root        67 2023-05-24 16:53 web/assets/default_header.png
-rw-r--r-- root/root      4444 2023-05-24 16:54 web/assets/dist/_colors.css
-rw-r--r-- root/root     14685 2023-05-24 16:54 web/assets/dist/base.css
-rw-r--r-- root/root    210344 2023-05-24 16:54 web/assets/dist/frontend.js
-rw-r--r-- root/root      1027 2023-05-24 16:54 web/assets/dist/index.css
-rw-r--r-- root/root      6251 2023-05-24 16:54 web/assets/dist/profile.css
-rw-r--r-- root/root     21000 2023-05-24 16:54 web/assets/dist/settings-style.css
-rw-r--r-- root/root   1153338 2023-05-24 16:54 web/assets/dist/settings.js
-rw-r--r-- root/root     52456 2023-05-24 16:54 web/assets/dist/status.css
-rw-r--r-- root/root      4350 2023-05-24 16:53 web/assets/fonts/OFL.txt
-rw-r--r-- root/root     16312 2023-05-24 16:53 web/assets/fonts/noto-sans-v27-latin-700.woff
-rw-r--r-- root/root     12684 2023-05-24 16:53 web/assets/fonts/noto-sans-v27-latin-700.woff2
-rw-r--r-- root/root     16592 2023-05-24 16:53 web/assets/fonts/noto-sans-v27-latin-regular.woff
-rw-r--r-- root/root     12860 2023-05-24 16:53 web/assets/fonts/noto-sans-v27-latin-regular.woff2
-rw-r--r-- root/root     27204 2023-05-24 16:53 web/assets/logo.png
-rw-r--r-- root/root     22267 2023-05-24 16:53 web/assets/logo.svg
-rw-r--r-- root/root      1499 2023-05-24 16:53 web/assets/mastodon.svg
-rw-r--r-- root/root      5785 2023-05-24 16:53 web/assets/plyr.svg
-rw-r--r-- root/root    153798 2023-05-24 16:54 web/assets/swagger.yaml
-rw-r--r-- root/root     16784 2023-05-24 16:53 web/assets/tusky.svg
-rw-r--r-- root/root      1390 2023-05-24 16:53 web/template/404.tmpl
-rw-r--r-- root/root      3022 2023-05-24 16:53 web/template/about.tmpl
-rw-r--r-- root/root      1550 2023-05-24 16:53 web/template/authorize.tmpl
-rw-r--r-- root/root      1022 2023-05-24 16:53 web/template/confirmed.tmpl
-rw-r--r-- root/root      1742 2023-05-24 16:53 web/template/domain-blocklist.tmpl
-rw-r--r-- root/root      1186 2023-05-24 16:53 web/template/email_confirm.tmpl
-rw-r--r-- root/root      1247 2023-05-24 16:53 web/template/email_new_report.tmpl
-rw-r--r-- root/root      1262 2023-05-24 16:53 web/template/email_report_closed.tmpl
-rw-r--r-- root/root      1154 2023-05-24 16:53 web/template/email_reset.tmpl
-rw-r--r-- root/root      1004 2023-05-24 16:53 web/template/email_test.tmpl
-rw-r--r-- root/root      1069 2023-05-24 16:53 web/template/error.tmpl
-rw-r--r-- root/root      2277 2023-05-24 16:53 web/template/finalize.tmpl
-rw-r--r-- root/root      1552 2023-05-24 16:53 web/template/footer.tmpl
-rw-r--r-- root/root       919 2023-05-24 16:53 web/template/frontend.tmpl
-rw-r--r-- root/root      3620 2023-05-24 16:53 web/template/header.tmpl
-rw-r--r-- root/root      3190 2023-05-24 16:53 web/template/index.tmpl
-rw-r--r-- root/root      1063 2023-05-24 16:53 web/template/oob.tmpl
-rw-r--r-- root/root      4516 2023-05-24 16:53 web/template/profile.tmpl
-rw-r--r-- root/root      1556 2023-05-24 16:53 web/template/sign-in.tmpl
-rw-r--r-- root/root      4465 2023-05-24 16:53 web/template/status.tmpl
-rw-r--r-- root/root      1287 2023-05-24 16:53 web/template/thread.tmpl

Based on the panic I'm sort of assuming I've misunderstood the documentation on how to use ExtraFilePaths but I'm struggling to find a repository that uses gokrazy and ExtraFilePaths with a tar bundle to see where I'm going wrong.

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.