Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Rewrite operator_ui/install as go file #13169

Merged
merged 6 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/stupid-poems-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

#internal rework operator_ui installer
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ tools/clroot/db.sqlite3-wal
*.iml
debug.env
*.txt
operator_ui/install

# codeship
*.aes
Expand Down Expand Up @@ -102,4 +103,3 @@ override*.toml
.venv/

ocr_soak_report.csv

2 changes: 1 addition & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ docker-plugins:

.PHONY: operator-ui
operator-ui: ## Fetch the frontend
go generate ./core/web
go run operator_ui/install.go .

.PHONY: abigen
abigen: ## Build & install abigen.
Expand Down
2 changes: 1 addition & 1 deletion core/web/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
// inside this module. To achieve this, we direct webpack to output all of the compiled assets
// in this module's folder under the "assets" directory.

//go:generate ../../operator_ui/install.sh
//go:generate go run ../../operator_ui/install.go ../..
//go:embed "assets"
var uiEmbedFs embed.FS

Expand Down
7 changes: 6 additions & 1 deletion operator_ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ This package is responsible for rendering the UI of the chainlink node, which al

### Requirements

The `install.sh` script handles installing the specified tag of operator UI within the [tag file](./TAG). When executed, it downloads then moves the static assets of operator UI into the `core/web/assets` path. Then, when the chainlink binary is built, these assets are included into the build that gets served.
The `install.go` script handles installing the specified tag of operator UI within the [tag file](./TAG). When executed, it downloads then moves the static assets of operator UI into the `core/web/assets` path. Then, when the chainlink binary is built, these assets are included into the build that gets served.

```sh
# The argument is the path from the this directory to the root of the repository
go run ./install.go ..
```

## Updates

Expand Down
162 changes: 162 additions & 0 deletions operator_ui/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package main

import (
"archive/tar"
"compress/gzip"
"context"
"fmt"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
)

func main() {
const (
owner = "smartcontractkit"
repo = "operator-ui"
fullRepo = owner + "/" + repo
tagPath = "operator_ui/TAG"
unpackDir = "core/web/assets"
downloadTimeoutSeconds = 10
)
// Grab first argument as root directory
if len(os.Args) < 2 {
log.Fatalln("Usage: install.go <root>")
}
rootDir := os.Args[1]

tag := mustReadTagFile(path.Join(rootDir, tagPath))
strippedTag := stripVersionFromTag(tag)
assetName := fmt.Sprintf("%s-%s-%s.tgz", owner, repo, strippedTag)
downloadUrl := fmt.Sprintf("https://github.com/%s/releases/download/%s/%s", fullRepo, tag, assetName)

// Assuming that we're in "root/operator_ui/"
unpackPath := filepath.Join(rootDir, unpackDir)
err := rmrf(unpackPath)
if err != nil {
log.Fatalln(err)
}

subPath := "package/artifacts/"
mustDownloadSubAsset(downloadUrl, downloadTimeoutSeconds, unpackPath, subPath)
}

func mustReadTagFile(file string) string {
tagBytes, err := os.ReadFile(file)
if err != nil {
log.Fatalln(err)
}
tag := string(tagBytes)
return strings.TrimSpace(tag)
}

func stripVersionFromTag(tag string) string {
return strings.TrimPrefix(tag, "v")
}

func rmrf(path string) error {
err := os.RemoveAll(path)
if err != nil {
return err
}

err = os.Mkdir(path, 0755)
return err
}

// Download a sub asset from a .tgz file and extract it to a destination path
func mustDownloadSubAsset(downloadUrl string, downloadTimeoutSeconds int, unpackPath string, subPath string) {
fmt.Println("Downloading", downloadUrl)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(downloadTimeoutSeconds)*time.Second)
defer cancel()
/* #nosec G107 */
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadUrl, nil)
if err != nil {
log.Fatalln(err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalln(err)
}

defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Fatalln(fmt.Errorf("failed to fetch asset: %s", resp.Status))
}

err = decompressTgzSubpath(resp.Body, unpackPath, subPath)
if err != nil {
log.Fatalln(err)
}
}

// Decompress a .tgz file to a destination path, only extracting files that are in the subpath
//
// Subpath files are extracted to the root of the destination path, rather than preserving the subpath
func decompressTgzSubpath(file io.Reader, destPath string, subPath string) error {
// Create a gzip reader
gzr, err := gzip.NewReader(file)
if err != nil {
return fmt.Errorf("failed to create gzip reader: %w", err)
}
defer gzr.Close()

// Create a tar reader
tr := tar.NewReader(gzr)

// Iterate through the files in the tar archive
for {
header, err := tr.Next()
switch {
case err == io.EOF:
return nil // End of tar archive
case err != nil:
return fmt.Errorf("failed to read tar file: %w", err)
case header == nil:
continue
}
// skip files that arent in the subpath
if !strings.HasPrefix(header.Name, subPath) {
continue
}

// Strip the subpath from the header name
header.Name = strings.TrimPrefix(header.Name, subPath)

// Target location where the dir/file should be created
target := fmt.Sprintf("%s/%s", destPath, header.Name)

// Check the file type
switch header.Typeflag {
case tar.TypeDir: // Directory
if err := os.MkdirAll(target, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
fmt.Println("Creating directory", target)
case tar.TypeReg: // Regular file
if err := writeFile(target, header, tr); err != nil {
return err
}
}
}
}

func writeFile(target string, header *tar.Header, tr *tar.Reader) error {
/* #nosec G110 */
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer f.Close()

if _, err := io.Copy(f, tr); err != nil {
return fmt.Errorf("failed to write file: %w", err)
}
fmt.Println("Creating file", target)
return nil
}
55 changes: 0 additions & 55 deletions operator_ui/install.sh

This file was deleted.

Loading