pkg/make-release/js_packager.go
Rama main · réplica de lectura del repositorio original.
package main
import (
"io/fs"
osPkg "os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"sync/atomic"
"github.com/cockroachdb/errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
type JSPackager struct {
log zerolog.Logger
WorkspaceRoot string // Path to the yarn workspace root
Version string // the new version number
DistFolder string // the folder to output the compiled JS to
compileCompleted chan struct{}
compileFailed atomic.Bool
}
func (j *JSPackager) PatchVersions() error {
j.log.Info().Msg("Patching versions...")
replaceVersionRegex, err := regexp.Compile(`"version": "([^-"]+)(?:-[^"]+)?",`)
if err != nil {
j.log.Err(err).Msg("failed to compile regex")
return errors.Wrap(err, "compile regex")
}
// For all the folders in the packages dir
packages, err := j.listPackages(filepath.Join(j.WorkspaceRoot, "packages"), "")
if err != nil {
return errors.Wrap(err, "list packages")
}
for _, pkg := range packages {
// Look for a line containing `"version": ".*"`, and replace it
// with `"version": "0.0.0"`.
// (note we drop the "v" prefix for the package.json)
newFile := replaceVersionRegex.ReplaceAll(pkg.PackageJsonData, []byte(`"version": "`+j.Version[1:]+`",`))
// Write the file back to disk
err = osPkg.WriteFile(pkg.PackageJsonPath, newFile, 0644)
if err != nil {
j.log.Err(err).Str("package", pkg.Name).Msg("failed to write package.json")
return errors.Wrap(err, "write package.json")
}
}
j.log.Info().Msg("Patching internal-runtime/conf/version.ts...")
err = osPkg.WriteFile(
filepath.Join(j.WorkspaceRoot, "packages", "@encore.dev", "internal-runtime", "conf", "version.ts"),
[]byte(`// Code generated by /pkg/make-release. DO NOT EDIT.
/**
* The current version of the runtime.
*/
export const Version = "`+j.Version+`";
`),
0644,
)
if err != nil {
j.log.Err(err).Msg("failed to patch version.ts")
return errors.Wrap(err, "write patch version.ts")
}
return nil
}
type packageInfo struct {
RootDir string
Name string
PackageJsonPath string
PackageJsonData []byte
}
func (j *JSPackager) listPackages(dir string, basePkgName string) ([]packageInfo, error) {
var pkgs []packageInfo
// For all the folders in the packages dir
entries, err := osPkg.ReadDir(dir)
if err != nil {
j.log.Err(err).Str("dir", dir).Msg("failed to read directory")
return nil, errors.Wrap(err, "read directory")
}
for _, e := range entries {
pkgDir := filepath.Join(dir, e.Name())
switch {
case !e.IsDir():
continue
case strings.HasPrefix(e.Name(), "@"):
children, err := j.listPackages(pkgDir, e.Name())
if err != nil {
return nil, err
}
pkgs = append(pkgs, children...)
default:
pkgJsonPath := filepath.Join(pkgDir, "package.json")
pkgJson, err := osPkg.ReadFile(pkgJsonPath)
if errors.Is(err, fs.ErrNotExist) {
continue
} else if err != nil {
return nil, errors.Wrap(err, "read package.json")
}
pkgName := e.Name()
if basePkgName != "" {
pkgName = basePkgName + "/" + pkgName
}
pkgs = append(pkgs, packageInfo{
RootDir: pkgDir,
Name: pkgName,
PackageJsonPath: pkgJsonPath,
PackageJsonData: pkgJson,
})
}
}
return pkgs, nil
}
func (j *JSPackager) Package() (rtnErr error) {
defer func() {
if rtnErr != nil {
j.compileFailed.Store(true)
}
close(j.compileCompleted)
}()
j.DistFolder = filepath.Join(j.WorkspaceRoot, "dist")
// Remove the existing dist directory
if err := osPkg.RemoveAll(j.DistFolder); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
j.log.Err(err).Msg("failed to remove dist directory")
return errors.Wrap(err, "remove dist directory")
}
}
// Patch the versions
if err := j.PatchVersions(); err != nil {
j.log.Err(err).Msg("failed to patch versions")
return errors.Wrap(err, "patch versions")
}
// Ensure our node_modules is up to date and installed
log.Info().Msg("Installing JS dependencies...")
err := j.yarn("install")
if err != nil {
j.log.Err(err).Msg("yarn install failed")
return errors.Wrap(err, "yarn install")
}
// Now clean up previous builds inside yarn
log.Info().Msg("Cleaning up from previous builds...")
err = j.yarn("clean")
if err != nil {
j.log.Err(err).Msg("yarn clean failed")
return errors.Wrap(err, "yarn clean")
}
log.Info().Msg("Building JS...")
err = j.yarn("build", "--filter=./packages/**/*", "--force") // only build our packages
if err != nil {
j.log.Err(err).Msg("yarn build failed")
return errors.Wrap(err, "yarn build")
}
log.Info().Msg("Fixing dist folder for ESM")
err = j.yarn("fix-dist")
if err != nil {
j.log.Err(err).Msg("yarn fix-dist failed")
return errors.Wrap(err, "yarn fix-dist")
}
// Now it's all build we can start the publish/packing process
log.Info().Msg("Publishing packages to npm...")
npmTag := "latest"
switch {
case strings.Contains(j.Version, "-beta."):
npmTag = "beta"
case strings.Contains(j.Version, "-nightly."):
npmTag = "nightly"
}
err = j.yarn("workspaces", "foreach", "--no-private", "-pt", "npm", "publish", "--tolerate-republish", "--access", "public", "--tag", npmTag)
if err != nil {
j.log.Err(err).Msg("yarn publish failed")
return errors.Wrap(err, "yarn publish")
}
// Create our dist folder
err = osPkg.MkdirAll(j.DistFolder, 0755)
if err != nil {
j.log.Err(err).Msg("failed to create dist directory")
return errors.Wrap(err, "create dist directory")
}
// Package up our JS runtime
log.Info().Msg("Packaging JS runtime packages...")
err = j.yarn("workspaces", "foreach", "--no-private", "pack", "--out", filepath.Join(j.DistFolder, "%s.tgz"))
if err != nil {
j.log.Err(err).Msg("yarn pack failed")
return errors.Wrap(err, "yarn pack")
}
// Now extract all the tarballs in our dist folder, deleting the tarballs
// as we go.
log.Info().Msg("Extracting JS runtime packages...")
dirEntries, err := osPkg.ReadDir(j.DistFolder)
if err != nil {
j.log.Err(err).Msg("failed to read dist directory")
return errors.Wrap(err, "read dist directory")
}
for _, entry := range dirEntries {
if entry.IsDir() {
continue
}
if filepath.Ext(entry.Name()) != ".tgz" {
continue
}
name := entry.Name()
name = strings.TrimSuffix(name, filepath.Ext(name))
dirToExtractTo := filepath.Join(j.DistFolder, name)
dirToExtractTo = strings.ReplaceAll(dirToExtractTo, "@encore.dev-", "@encore.dev/")
// Create the target directory
err = osPkg.MkdirAll(dirToExtractTo, 0755)
if err != nil {
j.log.Err(err).Str("tgz_file", entry.Name()).Str("target", dirToExtractTo).Msg("failed to create target directory")
return errors.Wrap(err, "create target directory")
}
// Extract the tarball
out, err := exec.Command("tar", "-xzf", filepath.Join(j.DistFolder, entry.Name()), "--strip-components", "1", "-C", dirToExtractTo).CombinedOutput()
// nosemgrep
if err != nil {
j.log.Err(err).Str("tgz_file", entry.Name()).Str("target", dirToExtractTo).Str("output", string(out)).Msg("failed to extract tarball")
return errors.Wrap(err, "extract tarball")
}
// Delete the tarball
err = osPkg.Remove(filepath.Join(j.DistFolder, entry.Name()))
if err != nil {
j.log.Err(err).Str("tgz_file", entry.Name()).Str("target", dirToExtractTo).Msg("failed to delete tarball")
return errors.Wrap(err, "delete tarball")
}
}
log.Info().Msg("JS runtime packaged successfully")
return nil
}
func (j *JSPackager) yarn(args ...string) error {
cmd := exec.Command("yarn", args...)
cmd.Env = osPkg.Environ()
cmd.Dir = j.WorkspaceRoot
cmd.Stdout = osPkg.Stdout
cmd.Stderr = osPkg.Stderr
j.log.Debug().Str("cmd", cmd.String()).Msg("running yarn command...")
if err := cmd.Run(); err != nil {
return errors.Wrap(err, "yarn command failed")
}
return nil
}