mirror of
https://github.com/grafana/grafana.git
synced 2025-01-21 14:03:29 -06:00
Image Rendering: Remove PhantomJS support (#23460)
Removes all references and usage of PhantomJS #23375. Remove direct link rendered image e2e smoke test for now. Docker: Fix installing chrome in ubuntu custom docker image. Improve handling of image renderer not available/installed #23593. Add PhantomJS breaking change and upgrading notes. Use grabpl v0.2.10. Closes #13802 Co-authored-by: Kyle Brandt <kyle@grafana.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
This commit is contained in:
parent
0a1ab60b8c
commit
6e313e7d37
@ -50,7 +50,7 @@ commands:
|
||||
- run:
|
||||
name: "Install Grafana build pipeline tool"
|
||||
command: |
|
||||
VERSION=0.2.9
|
||||
VERSION=0.2.10
|
||||
curl -fLO https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${VERSION}/grabpl
|
||||
chmod +x grabpl
|
||||
mv grabpl /tmp
|
||||
|
@ -1,3 +1,9 @@
|
||||
# 7.0.0 (unreleased)
|
||||
|
||||
## Breaking changes
|
||||
|
||||
- **Removed PhantomJS**: PhantomJS was deprecated in [Grafana v6.4](https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-4/#phantomjs-deprecation) and starting from Grafana v7.0.0, all PhantomJS support has been removed. This means that Grafana no longer ships with a built-in image renderer, and we advise you to install the [Grafana Image Renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer).
|
||||
|
||||
# 6.7.2 (2020-04-02)
|
||||
|
||||
### Bug Fixes
|
||||
@ -6,7 +12,7 @@
|
||||
- **Dashboard**: Fixed issue with saving new dashboard after changing title . [#23104](https://github.com/grafana/grafana/pull/23104), [@dprokop](https://github.com/dprokop)
|
||||
- **DataLinks**: make sure we use the correct datapoint when dataset contains null value.. [#22981](https://github.com/grafana/grafana/pull/22981), [@mckn](https://github.com/mckn)
|
||||
- **Plugins**: Fixed issue for plugins that imported dateMath util . [#23069](https://github.com/grafana/grafana/pull/23069), [@mckn](https://github.com/mckn)
|
||||
- **Security**: Fix for dashboard snapshot original dashboard link could contain XSS vulnerability in url. [#23254](https://github.com/grafana/grafana/pull/23254), [@torkelo](https://github.com/torkelo). Big thanks to Ahmed A. Sherif for reporting this issue.
|
||||
- **Security**: Fix for dashboard snapshot original dashboard link could contain XSS vulnerability in url. [#23254](https://github.com/grafana/grafana/pull/23254), [@torkelo](https://github.com/torkelo). Big thanks to Ahmed A. Sherif for reporting this issue.
|
||||
- **Variables**: Fixes issue with too many queries being issued for nested template variables after value change. [#23220](https://github.com/grafana/grafana/pull/23220), [@torkelo](https://github.com/torkelo)
|
||||
- **Plugins**: Expose promiseToDigest. [#23249](https://github.com/grafana/grafana/pull/23249), [@torkelo](https://github.com/torkelo)
|
||||
- **Reporting (Enterprise)**: Fixes issue updating a report created by someone else
|
||||
|
19
Dockerfile
19
Dockerfile
@ -18,16 +18,6 @@ RUN go run build.go build
|
||||
# Node build container
|
||||
FROM node:12.13.0-alpine
|
||||
|
||||
# PhantomJS
|
||||
RUN apk add --no-cache curl &&\
|
||||
cd /tmp && curl -Ls https://github.com/dustinblackman/phantomized/releases/download/2.1.1/dockerized-phantomjs.tar.gz | tar xz &&\
|
||||
cp -R lib lib64 / &&\
|
||||
cp -R usr/lib/x86_64-linux-gnu /usr/lib &&\
|
||||
cp -R usr/share /usr/share &&\
|
||||
cp -R etc/fonts /etc &&\
|
||||
curl -k -Ls https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 | tar -jxf - &&\
|
||||
cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
@ -80,18 +70,9 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
|
||||
chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \
|
||||
chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING"
|
||||
|
||||
# PhantomJS
|
||||
COPY --from=1 /tmp/lib /lib
|
||||
COPY --from=1 /tmp/lib64 /lib64
|
||||
COPY --from=1 /tmp/usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
|
||||
COPY --from=1 /tmp/usr/share /usr/share
|
||||
COPY --from=1 /tmp/etc/fonts /etc/fonts
|
||||
COPY --from=1 /usr/local/bin/phantomjs /usr/local/bin
|
||||
|
||||
COPY --from=0 /go/src/github.com/grafana/grafana/bin/linux-amd64/grafana-server /go/src/github.com/grafana/grafana/bin/linux-amd64/grafana-cli ./bin/
|
||||
COPY --from=1 /usr/src/app/public ./public
|
||||
COPY --from=1 /usr/src/app/tools ./tools
|
||||
COPY tools/phantomjs/render.js ./tools/phantomjs/render.js
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
|
@ -14,11 +14,6 @@ RUN go run build.go build
|
||||
|
||||
FROM node:12.13 AS js-builder
|
||||
|
||||
# PhantomJS
|
||||
RUN apt-get update && apt-get install -y curl &&\
|
||||
curl -L https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 | tar xj &&\
|
||||
cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
@ -54,8 +49,8 @@ WORKDIR $GF_PATHS_HOME
|
||||
|
||||
COPY conf conf
|
||||
|
||||
# We need font libs for phantomjs, and curl should be part of the image
|
||||
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates libfontconfig1 curl
|
||||
# curl should be part of the image
|
||||
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl
|
||||
|
||||
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
|
||||
addgroup --system --gid $GF_GID grafana && \
|
||||
@ -71,14 +66,11 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
|
||||
chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \
|
||||
chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING"
|
||||
|
||||
# PhantomJS
|
||||
COPY --from=js-builder /usr/local/bin/phantomjs /usr/local/bin/
|
||||
|
||||
COPY --from=go-builder /src/grafana/bin/linux-amd64/grafana-server /src/grafana/bin/linux-amd64/grafana-cli bin/
|
||||
COPY --from=js-builder /usr/src/app/public public
|
||||
COPY --from=js-builder /usr/src/app/tools tools
|
||||
|
||||
COPY tools/phantomjs/render.js tools/phantomjs/
|
||||
COPY packaging/docker/run.sh /
|
||||
|
||||
USER grafana
|
||||
|
@ -35,7 +35,6 @@ module.exports = function (grunt) {
|
||||
config.libc = grunt.option('libc');
|
||||
}
|
||||
|
||||
config.phjs = grunt.option('phjsToRelease');
|
||||
config.pkg.version = grunt.option('pkgVer') || config.pkg.version;
|
||||
|
||||
console.log('Version', config.pkg.version);
|
||||
|
5
build.go
5
build.go
@ -40,7 +40,6 @@ var (
|
||||
linuxPackageVersion string = "v1"
|
||||
linuxPackageIteration string = ""
|
||||
race bool
|
||||
phjsToRelease string
|
||||
workingDir string
|
||||
includeBuildId bool = true
|
||||
buildId string = "0"
|
||||
@ -69,7 +68,6 @@ func main() {
|
||||
flag.StringVar(&libc, "libc", "", "LIBC")
|
||||
flag.BoolVar(&cgo, "cgo-enabled", cgo, "Enable cgo")
|
||||
flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
|
||||
flag.StringVar(&phjsToRelease, "phjs", "", "PhantomJS binary")
|
||||
flag.BoolVar(&race, "race", race, "Use race detector")
|
||||
flag.BoolVar(&modVendor, "modVendor", modVendor, "Go modules use vendor folder")
|
||||
flag.BoolVar(&includeBuildId, "includeBuildId", includeBuildId, "IncludeBuildId in package name")
|
||||
@ -459,9 +457,6 @@ func gruntBuildArg(task string) []string {
|
||||
if libc != "" {
|
||||
args = append(args, fmt.Sprintf("--libc=%s", libc))
|
||||
}
|
||||
if phjsToRelease != "" {
|
||||
args = append(args, fmt.Sprintf("--phjsToRelease=%v", phjsToRelease))
|
||||
}
|
||||
if enterprise {
|
||||
args = append(args, "--enterprise")
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ The following example describes how to build and run the remote HTTP rendering s
|
||||
|
||||
## PhantomJS
|
||||
|
||||
> PhantomJS is deprecated since Grafana v6.4 and will be removed in a future release. Please migrate to the Grafana Image Renderer plugin or remote rendering service.
|
||||
> Starting from Grafana v7.0.0, all PhantomJS support has been removed. Please use the Grafana Image Renderer plugin or remote rendering service.
|
||||
|
||||
## Troubleshoot image rendering
|
||||
|
||||
|
@ -238,3 +238,9 @@ Due to this change in Chrome, the `[security]` setting `cookie_samesite` configu
|
||||
|
||||
This version of Chrome also rejects insecure `SameSite=None` cookies. See https://www.chromestatus.com/feature/5633521622188032 for more information. Make sure that you
|
||||
change the `[security]` setting `cookie_secure` to `true` and use HTTPS when `cookie_samesite` is configured to `none`, otherwise authentication in Grafana won't work properly.
|
||||
|
||||
## Upgrading to v7.0
|
||||
|
||||
### PhantomJS removed
|
||||
|
||||
PhantomJS was deprecated in [Grafana v6.4](https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-4/#phantomjs-deprecation) and starting from Grafana v7.0.0, all PhantomJS support has been removed. This means that Grafana no longer ships with a built-in image renderer, and we adwise you to install the [Grafana Image Renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer).
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 26 KiB |
@ -21,8 +21,6 @@ e2e.scenario({
|
||||
.contains('Show')
|
||||
.click();
|
||||
|
||||
e2e.pages.Components.BackButton.backArrow().click();
|
||||
|
||||
// e2e.pages.Dashboard.Panels.Panel.title('Panel Title').click();
|
||||
// e2e.pages.Dashboard.Panels.Panel.headerItems('Inspect').click();
|
||||
},
|
||||
|
@ -172,7 +172,6 @@
|
||||
"ngtemplate-loader": "2.0.1",
|
||||
"node-sass": "4.13.1",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||
"phantomjs-prebuilt": "2.1.16",
|
||||
"pixelmatch": "5.1.0",
|
||||
"pngjs": "3.4.0",
|
||||
"postcss-browser-reporter": "0.6.0",
|
||||
|
@ -104,7 +104,7 @@ export class GrafanaBootConfig {
|
||||
tracingIntegration: false,
|
||||
};
|
||||
licenseInfo: LicenseInfo = {} as LicenseInfo;
|
||||
phantomJSRenderer = false;
|
||||
rendererAvailable = false;
|
||||
|
||||
constructor(options: GrafanaBootConfig) {
|
||||
this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark);
|
||||
|
@ -41,19 +41,6 @@ RUN if [ `arch` = "x86_64" ]; then \
|
||||
rm -f /etc/ld.so.cache; \
|
||||
fi
|
||||
|
||||
# PhantomJS
|
||||
RUN if [ `arch` = "x86_64" ]; then \
|
||||
apk add --no-cache --virtual phantomjs-utils curl && \
|
||||
cd /tmp && \
|
||||
curl -Ls https://github.com/dustinblackman/phantomized/releases/download/2.1.1/dockerized-phantomjs.tar.gz | tar xz && \
|
||||
cp -R lib lib64 / && \
|
||||
cp -R usr/lib/x86_64-linux-gnu /usr/lib && \
|
||||
cp -R usr/share/fonts /usr/share && \
|
||||
cp -R etc/fonts /etc && \
|
||||
rm -rf /tmp/* && \
|
||||
apk del --no-cache phantomjs-utils; \
|
||||
fi
|
||||
|
||||
COPY --from=0 /tmp/grafana "$GF_PATHS_HOME"
|
||||
|
||||
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
|
||||
|
@ -17,15 +17,17 @@ RUN mkdir -p "$GF_PATHS_PLUGINS" && \
|
||||
RUN if [ $GF_INSTALL_IMAGE_RENDERER_PLUGIN = "true" ]; then \
|
||||
apt-get update && \
|
||||
apt-get upgrade -y && \
|
||||
apt-get install -y chromium-browser && \
|
||||
apt-get install -y gdebi-core && \
|
||||
cd /tmp && \
|
||||
curl -LO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
|
||||
gdebi --n google-chrome-stable_current_amd64.deb && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
rm -rf /usr/share/grafana/tools/phantomjs; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
USER grafana
|
||||
|
||||
ENV GF_RENDERER_PLUGIN_CHROME_BIN="/usr/bin/chromium-browser"
|
||||
ENV GF_RENDERER_PLUGIN_CHROME_BIN="/usr/bin/google-chrome"
|
||||
|
||||
RUN if [ $GF_INSTALL_IMAGE_RENDERER_PLUGIN = "true" ]; then \
|
||||
grafana-cli \
|
||||
|
@ -27,14 +27,9 @@ ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bi
|
||||
WORKDIR $GF_PATHS_HOME
|
||||
|
||||
# Install dependencies
|
||||
# We need curl in the image, and if the architecture is x86-64, we need to install libfontconfig1 for PhantomJS
|
||||
RUN if [ `arch` = "x86_64" ]; then \
|
||||
apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates libfontconfig1 curl && \
|
||||
apt-get autoremove -y && rm -rf /var/lib/apt/lists/*; \
|
||||
else \
|
||||
apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl && \
|
||||
apt-get autoremove -y && rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
# We need curl in the image
|
||||
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl && \
|
||||
apt-get autoremove -y && rm -rf /var/lib/apt/lists/*;
|
||||
|
||||
COPY --from=grafana-builder /tmp/grafana "$GF_PATHS_HOME"
|
||||
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -212,7 +211,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
|
||||
},
|
||||
"featureToggles": hs.Cfg.FeatureToggles,
|
||||
"phantomJSRenderer": rendering.IsPhantomJSEnabled,
|
||||
"rendererAvailable": hs.RenderService.IsAvailable(),
|
||||
}
|
||||
|
||||
return jsonObj, nil
|
||||
|
@ -42,7 +42,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
appURL := setting.AppUrl
|
||||
appSubURL := setting.AppSubUrl
|
||||
|
||||
// special case when doing localhost call from phantomjs
|
||||
// special case when doing localhost call from image renderer
|
||||
if c.IsRenderCall && !hs.Cfg.ServeFromSubPath {
|
||||
appURL = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
|
||||
appSubURL = ""
|
||||
|
@ -53,19 +53,25 @@ func (n *notificationService) SendIfNeeded(evalCtx *EvalContext) error {
|
||||
}
|
||||
|
||||
if notifierStates.ShouldUploadImage() {
|
||||
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
|
||||
uploadEvalCtx := *evalCtx
|
||||
timeout := setting.AlertingNotificationTimeout / 2
|
||||
var uploadCtxCancel func()
|
||||
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
|
||||
if n.renderService.IsAvailable() {
|
||||
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
|
||||
uploadEvalCtx := *evalCtx
|
||||
timeout := setting.AlertingNotificationTimeout / 2
|
||||
var uploadCtxCancel func()
|
||||
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
|
||||
|
||||
// Try to upload the image without consuming all the time allocated for EvalContext
|
||||
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
|
||||
n.log.Error("Failed to render and upload alert panel image.", "ruleId", uploadEvalCtx.Rule.ID, "error", err)
|
||||
// Try to upload the image without consuming all the time allocated for EvalContext
|
||||
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
|
||||
n.log.Error("Failed to render and upload alert panel image.", "ruleId", uploadEvalCtx.Rule.ID, "error", err)
|
||||
}
|
||||
uploadCtxCancel()
|
||||
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
|
||||
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
|
||||
} else {
|
||||
n.log.Warn("Could not render image for alert notification, no image renderer found/installed. " +
|
||||
"For image rendering support please install the grafana-image-renderer plugin. " +
|
||||
"Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/")
|
||||
}
|
||||
uploadCtxCancel()
|
||||
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
|
||||
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
|
||||
}
|
||||
|
||||
return n.sendNotifications(evalCtx, notifierStates)
|
||||
|
@ -39,6 +39,16 @@ func TestNotificationService(t *testing.T) {
|
||||
require.Truef(t, evalCtx.Ctx.Value(notificationSent{}).(bool), "expected notification to be sent, but wasn't")
|
||||
})
|
||||
|
||||
notificationServiceScenario(t, "Given alert rule with upload image enabled but no renderer available should not render and upload image, but send notification", evalCtx, true, func(scenarioCtx *scenarioContext) {
|
||||
scenarioCtx.rendererAvailable = false
|
||||
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equalf(t, 0, scenarioCtx.renderCount, "expected render to not be called, but it was")
|
||||
require.Equalf(t, 0, scenarioCtx.imageUploadCount, "expected image to not be uploaded, but it was")
|
||||
require.Truef(t, evalCtx.Ctx.Value(notificationSent{}).(bool), "expected notification to be sent, but wasn't")
|
||||
})
|
||||
|
||||
notificationServiceScenario(t, "Given alert rule with upload image disabled should not render and upload image, but send notification", evalCtx, false, func(scenarioCtx *scenarioContext) {
|
||||
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
|
||||
require.NoError(t, err)
|
||||
@ -114,6 +124,7 @@ type scenarioContext struct {
|
||||
renderCount int
|
||||
uploadProvider func(ctx context.Context, path string) (string, error)
|
||||
renderProvider func(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error)
|
||||
rendererAvailable bool
|
||||
}
|
||||
|
||||
type scenarioFunc func(c *scenarioContext)
|
||||
@ -197,7 +208,12 @@ func notificationServiceScenario(t *testing.T, name string, evalCtx *EvalContext
|
||||
return &rendering.RenderResult{FilePath: "image.png"}, nil
|
||||
}
|
||||
|
||||
scenarioCtx.rendererAvailable = true
|
||||
|
||||
renderService := &testRenderService{
|
||||
isAvailableProvider: func() bool {
|
||||
return scenarioCtx.rendererAvailable
|
||||
},
|
||||
renderProvider: func(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error) {
|
||||
if scenarioCtx.renderProvider != nil {
|
||||
if _, err := scenarioCtx.renderProvider(ctx, opts); err != nil {
|
||||
@ -286,10 +302,19 @@ func (n *testNotifier) GetFrequency() time.Duration {
|
||||
var _ Notifier = &testNotifier{}
|
||||
|
||||
type testRenderService struct {
|
||||
isAvailableProvider func() bool
|
||||
renderProvider func(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error)
|
||||
renderErrorImageProvider func(error error) (*rendering.RenderResult, error)
|
||||
}
|
||||
|
||||
func (s *testRenderService) IsAvailable() bool {
|
||||
if s.isAvailableProvider != nil {
|
||||
return s.isAvailableProvider()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *testRenderService) Render(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error) {
|
||||
if s.renderProvider != nil {
|
||||
return s.renderProvider(ctx, opts)
|
||||
|
@ -32,6 +32,7 @@ type RenderResult struct {
|
||||
type renderFunc func(ctx context.Context, renderKey string, options Opts) (*RenderResult, error)
|
||||
|
||||
type Service interface {
|
||||
IsAvailable() bool
|
||||
Render(ctx context.Context, opts Opts) (*RenderResult, error)
|
||||
RenderErrorImage(error error) (*RenderResult, error)
|
||||
GetRenderUser(key string) (*RenderUser, bool)
|
||||
|
@ -1,129 +0,0 @@
|
||||
package rendering
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
func (rs *RenderingService) renderViaPhantomJS(ctx context.Context, renderKey string, opts Opts) (*RenderResult, error) {
|
||||
var executable = "phantomjs"
|
||||
if runtime.GOOS == "windows" {
|
||||
executable = executable + ".exe"
|
||||
}
|
||||
|
||||
url := rs.getURL(opts.Path)
|
||||
binPath, _ := filepath.Abs(filepath.Join(rs.Cfg.PhantomDir, executable))
|
||||
if _, err := os.Stat(binPath); os.IsNotExist(err) {
|
||||
rs.log.Error("executable not found", "executable", binPath)
|
||||
return nil, ErrPhantomJSNotInstalled
|
||||
}
|
||||
|
||||
scriptPath, _ := filepath.Abs(filepath.Join(rs.Cfg.PhantomDir, "render.js"))
|
||||
pngPath, err := rs.getFilePathForNewImage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
phantomDebugArg := "--debug=false"
|
||||
if log.GetLogLevelFor("rendering") >= log.LvlDebug {
|
||||
phantomDebugArg = "--debug=true"
|
||||
}
|
||||
|
||||
cmdArgs := []string{
|
||||
"--ignore-ssl-errors=true",
|
||||
"--web-security=true",
|
||||
"--local-url-access=false",
|
||||
phantomDebugArg,
|
||||
scriptPath,
|
||||
fmt.Sprintf("url=%v", url),
|
||||
fmt.Sprintf("width=%v", opts.Width),
|
||||
fmt.Sprintf("height=%v", opts.Height),
|
||||
fmt.Sprintf("png=%v", pngPath),
|
||||
fmt.Sprintf("domain=%v", rs.domain),
|
||||
fmt.Sprintf("timeout=%v", opts.Timeout.Seconds()),
|
||||
fmt.Sprintf("renderKey=%v", renderKey),
|
||||
}
|
||||
|
||||
if opts.Encoding != "" {
|
||||
cmdArgs = append([]string{fmt.Sprintf("--output-encoding=%s", opts.Encoding)}, cmdArgs...)
|
||||
}
|
||||
|
||||
// gives phantomjs some additional time to timeout and return possible errors.
|
||||
commandCtx, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(commandCtx, binPath, cmdArgs...)
|
||||
cmd.Stderr = cmd.Stdout
|
||||
|
||||
timezone := ""
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
if opts.Timezone != "" {
|
||||
timezone = isoTimeOffsetToPosixTz(opts.Timezone)
|
||||
cmd.Env = appendEnviron(cmd.Env, "TZ", timezone)
|
||||
}
|
||||
|
||||
// Added to disable usage of newer version of OPENSSL
|
||||
// that seem to be incompatible with PhantomJS (used in Debian Buster)
|
||||
if runtime.GOOS == "linux" {
|
||||
disableNewOpenssl := "/etc/ssl"
|
||||
cmd.Env = appendEnviron(cmd.Env, "OPENSSL_CONF", disableNewOpenssl)
|
||||
}
|
||||
|
||||
rs.log.Debug("executing Phantomjs", "binPath", binPath, "cmdArgs", cmdArgs, "timezone", timezone)
|
||||
|
||||
out, err := cmd.Output()
|
||||
|
||||
if out != nil {
|
||||
rs.log.Debug("Phantomjs output", "out", string(out))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
rs.log.Debug("Phantomjs error", "error", err)
|
||||
}
|
||||
|
||||
// check for timeout first
|
||||
if commandCtx.Err() == context.DeadlineExceeded {
|
||||
rs.log.Info("Rendering timed out")
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
rs.log.Error("Phantomjs exited with non zero exit code", "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rs.log.Debug("Image rendered", "path", pngPath)
|
||||
return &RenderResult{FilePath: pngPath}, nil
|
||||
}
|
||||
|
||||
func isoTimeOffsetToPosixTz(isoOffset string) string {
|
||||
// invert offset
|
||||
if strings.HasPrefix(isoOffset, "UTC+") {
|
||||
return strings.Replace(isoOffset, "UTC+", "UTC-", 1)
|
||||
}
|
||||
if strings.HasPrefix(isoOffset, "UTC-") {
|
||||
return strings.Replace(isoOffset, "UTC-", "UTC+", 1)
|
||||
}
|
||||
return isoOffset
|
||||
}
|
||||
|
||||
func appendEnviron(baseEnviron []string, name string, value string) []string {
|
||||
results := make([]string, 0)
|
||||
prefix := fmt.Sprintf("%s=", name)
|
||||
for _, v := range baseEnviron {
|
||||
if !strings.HasPrefix(v, prefix) {
|
||||
results = append(results, v)
|
||||
}
|
||||
}
|
||||
return append(results, fmt.Sprintf("%s=%s", name, value))
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
@ -23,8 +24,6 @@ func init() {
|
||||
registry.RegisterService(&RenderingService{})
|
||||
}
|
||||
|
||||
var IsPhantomJSEnabled = false
|
||||
|
||||
const renderKeyPrefix = "render-%s"
|
||||
|
||||
type RenderUser struct {
|
||||
@ -76,30 +75,31 @@ func (rs *RenderingService) Run(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if plugins.Renderer == nil {
|
||||
rs.log = rs.log.New("renderer", "phantomJS")
|
||||
rs.log.Info("Backend rendering via phantomJS")
|
||||
rs.log.Warn("phantomJS is deprecated and will be removed in a future release. " +
|
||||
"You should consider migrating from phantomJS to grafana-image-renderer plugin. " +
|
||||
"Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/")
|
||||
rs.renderAction = rs.renderViaPhantomJS
|
||||
IsPhantomJSEnabled = true
|
||||
if plugins.Renderer != nil {
|
||||
rs.log = rs.log.New("renderer", "plugin")
|
||||
rs.pluginInfo = plugins.Renderer
|
||||
|
||||
if err := rs.startPlugin(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rs.renderAction = rs.renderViaPlugin
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
rs.log = rs.log.New("renderer", "plugin")
|
||||
rs.pluginInfo = plugins.Renderer
|
||||
rs.log.Debug("No image renderer found/installed. " +
|
||||
"For image rendering support please install the grafana-image-renderer plugin. " +
|
||||
"Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/")
|
||||
|
||||
if err := rs.startPlugin(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rs.renderAction = rs.renderViaPlugin
|
||||
<-ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *RenderingService) IsAvailable() bool {
|
||||
return rs.renderAction != nil
|
||||
}
|
||||
|
||||
func (rs *RenderingService) RenderErrorImage(err error) (*RenderResult, error) {
|
||||
imgUrl := "public/img/rendering_error.png"
|
||||
|
||||
@ -209,3 +209,14 @@ func (rs *RenderingService) deleteRenderKey(key string) {
|
||||
rs.log.Error("Failed to delete render key", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func isoTimeOffsetToPosixTz(isoOffset string) string {
|
||||
// invert offset
|
||||
if strings.HasPrefix(isoOffset, "UTC+") {
|
||||
return strings.Replace(isoOffset, "UTC+", "UTC-", 1)
|
||||
}
|
||||
if strings.HasPrefix(isoOffset, "UTC-") {
|
||||
return strings.Replace(isoOffset, "UTC-", "UTC+", 1)
|
||||
}
|
||||
return isoOffset
|
||||
}
|
||||
|
@ -239,7 +239,6 @@ type Cfg struct {
|
||||
|
||||
// Rendering
|
||||
ImagesDir string
|
||||
PhantomDir string
|
||||
RendererUrl string
|
||||
RendererCallbackUrl string
|
||||
RendererLimit int
|
||||
@ -940,7 +939,6 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
}
|
||||
}
|
||||
cfg.ImagesDir = filepath.Join(cfg.DataPath, "png")
|
||||
cfg.PhantomDir = filepath.Join(HomePath, "tools/phantomjs")
|
||||
cfg.TempDataLifetime = iniFile.Section("paths").Key("temp_data_lifetime").MustDuration(time.Second * 3600 * 24)
|
||||
cfg.MetricsEndpointEnabled = iniFile.Section("metrics").Key("enabled").MustBool(true)
|
||||
cfg.MetricsEndpointBasicAuthUsername, err = valueAsString(iniFile.Section("metrics"), "basic_auth_username", "")
|
||||
|
@ -17,7 +17,7 @@ export class Profiler {
|
||||
|
||||
renderingCompleted() {
|
||||
// add render counter to root scope
|
||||
// used by phantomjs render.js to know when panel has rendered
|
||||
// used by image renderer to know when panel has rendered
|
||||
this.panelsRendered = (this.panelsRendered || 0) + 1;
|
||||
|
||||
// this window variable is used by backend rendering tools to know
|
||||
|
@ -6,6 +6,7 @@ import { SelectableValue, PanelModel, AppEvents } from '@grafana/data';
|
||||
import { DashboardModel } from 'app/features/dashboard/state';
|
||||
import { buildImageUrl, buildShareUrl } from './utils';
|
||||
import { appEvents } from 'app/core/core';
|
||||
import config from 'app/core/config';
|
||||
|
||||
const themeOptions: Array<SelectableValue<string>> = [
|
||||
{ label: 'current', value: 'current' },
|
||||
@ -127,7 +128,7 @@ export class ShareLink extends PureComponent<Props, State> {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{panel && (
|
||||
{panel && config.rendererAvailable && (
|
||||
<div className="gf-form">
|
||||
<a href={imageUrl} target="_blank" aria-label={selectors.linkToRenderedImage}>
|
||||
<Icon name="camera" /> Direct link rendered image
|
||||
|
@ -255,7 +255,7 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||
const { theme } = config;
|
||||
|
||||
// This is only done to increase a counter that is used by backend
|
||||
// image rendering (phantomjs/headless chrome) to know when to capture image
|
||||
// image rendering to know when to capture image
|
||||
const loading = data.state;
|
||||
if (loading === LoadingState.Done) {
|
||||
profiler.renderingCompleted();
|
||||
|
@ -166,11 +166,6 @@ export function grafanaAppDirective(
|
||||
elem.toggleClass('view-mode--playlist', false);
|
||||
});
|
||||
|
||||
// check if we are in server side render
|
||||
if (config.phantomJSRenderer && document.cookie.indexOf('renderKey') !== -1) {
|
||||
body.addClass('body--phantomjs');
|
||||
}
|
||||
|
||||
// tooltip removal fix
|
||||
// manage page classes
|
||||
let pageClass: string;
|
||||
|
@ -118,29 +118,6 @@
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs {
|
||||
.graph-panel {
|
||||
display: -webkit-box;
|
||||
}
|
||||
|
||||
.graph-panel--legend-right {
|
||||
.graph-legend {
|
||||
display: block;
|
||||
max-width: min-content;
|
||||
}
|
||||
|
||||
.graph-panel__chart {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
display: table;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
padding-bottom: 1px;
|
||||
padding-right: 5px;
|
||||
|
@ -275,11 +275,6 @@
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs .baron__track .baron__bar {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.baron__control {
|
||||
display: none;
|
||||
}
|
||||
|
@ -86,13 +86,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// fix for phantomjs
|
||||
.body--phantomjs {
|
||||
.scroll-canvas {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.page-body {
|
||||
padding-top: $spacer * 2;
|
||||
padding-bottom: $spacer * 4;
|
||||
|
@ -90,8 +90,6 @@ source /etc/profile.d/rvm.sh
|
||||
echo "Packaging"
|
||||
go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only
|
||||
go run build.go -goos linux -pkg-arch amd64 -libc musl ${OPT} -skipRpm -skipDeb package-only
|
||||
#removing amd64 phantomjs bin for armv7/arm64 packages
|
||||
rm tools/phantomjs/phantomjs
|
||||
|
||||
go run build.go -goos linux -pkg-arch armv6 ${OPT} -skipRpm package-only
|
||||
go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only
|
||||
@ -99,20 +97,8 @@ go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only
|
||||
go run build.go -goos linux -pkg-arch armv7 -libc musl ${OPT} -skipRpm -skipDeb package-only
|
||||
go run build.go -goos linux -pkg-arch arm64 -libc musl ${OPT} -skipRpm -skipDeb package-only
|
||||
|
||||
if [ -d '/tmp/phantomjs/darwin' ]; then
|
||||
cp /tmp/phantomjs/darwin/phantomjs tools/phantomjs/phantomjs
|
||||
else
|
||||
echo 'PhantomJS binaries for darwin missing!'
|
||||
fi
|
||||
go run build.go -goos darwin -pkg-arch amd64 ${OPT} package-only
|
||||
|
||||
if [ -d '/tmp/phantomjs/windows' ]; then
|
||||
cp /tmp/phantomjs/windows/phantomjs.exe tools/phantomjs/phantomjs.exe
|
||||
rm tools/phantomjs/phantomjs || true
|
||||
else
|
||||
echo 'PhantomJS binaries for Windows missing!'
|
||||
fi
|
||||
|
||||
cp /usr/local/go/lib/time/zoneinfo.zip tools/zoneinfo.zip
|
||||
go run build.go -goos windows -pkg-arch amd64 ${OPT} package-only
|
||||
rm tools/zoneinfo.zip
|
||||
|
@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
if [ ! -d '/tmp/phantomjs' ]; then
|
||||
_version="2.1.1"
|
||||
|
||||
curl -fL https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-$_version-windows.zip -o /tmp/phantomjs-win.zip
|
||||
[ "$(sha256sum /tmp/phantomjs-win.zip | cut -d' ' -f1)" = \
|
||||
"d9fb05623d6b26d3654d008eab3adafd1f6350433dfd16138c46161f42c7dcc8" ] || \
|
||||
(echo "Checksum mismatch phantomjs-$_version-windows.zip"; exit 1)
|
||||
curl -fL https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-$_version-macosx.zip -o /tmp/phantomjs-mac.zip
|
||||
[ "$(sha256sum /tmp/phantomjs-mac.zip | cut -d' ' -f1)" = \
|
||||
"538cf488219ab27e309eafc629e2bcee9976990fe90b1ec334f541779150f8c1" ] || \
|
||||
(echo "Checksum mismatch phantomjs-$_version-mac.zip"; exit 1)
|
||||
|
||||
cd /tmp
|
||||
unzip /tmp/phantomjs-win.zip
|
||||
unzip /tmp/phantomjs-mac.zip
|
||||
|
||||
mkdir -p /tmp/phantomjs/windows /tmp/phantomjs/darwin
|
||||
|
||||
cp /tmp/phantomjs-$_version-windows/bin/phantomjs.exe /tmp/phantomjs/windows/phantomjs.exe
|
||||
cp /tmp/phantomjs-$_version-macosx/bin/phantomjs /tmp/phantomjs/darwin/phantomjs
|
||||
fi
|
@ -6,7 +6,6 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('build', [
|
||||
'clean:release',
|
||||
'clean:build',
|
||||
'phantomjs',
|
||||
'exec:webpack',
|
||||
]);
|
||||
|
||||
|
@ -5,7 +5,6 @@ module.exports = function(grunt) {
|
||||
// prettier-ignore
|
||||
grunt.registerTask('default', [
|
||||
'clean:build',
|
||||
'phantomjs',
|
||||
]);
|
||||
|
||||
// prettier-ignore
|
||||
|
@ -1,33 +0,0 @@
|
||||
module.exports = function(config,grunt) {
|
||||
'use strict';
|
||||
|
||||
grunt.registerTask('phantomjs', 'Copy phantomjs binary to vendor/', function() {
|
||||
var dest = './tools/phantomjs/phantomjs';
|
||||
var confDir = './node_modules/phantomjs-prebuilt/lib/';
|
||||
|
||||
if (process.platform === "win32") {
|
||||
dest += ".exe";
|
||||
}
|
||||
|
||||
var src = config.phjs;
|
||||
if (!src) {
|
||||
var m = grunt.file.read(confDir+"location.js");
|
||||
src = /= \"([^\"]*)\"/.exec(m)[1];
|
||||
if (!grunt.file.isPathAbsolute(src)) {
|
||||
src = confDir+src;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
grunt.config('copy.phantom_bin', {
|
||||
src: src,
|
||||
dest: dest,
|
||||
options: { mode: true},
|
||||
});
|
||||
grunt.task.run('copy:phantom_bin');
|
||||
} catch (err) {
|
||||
grunt.verbose.writeln(err);
|
||||
grunt.fail.warn('No working Phantomjs binary available')
|
||||
}
|
||||
});
|
||||
};
|
0
tools/.gitkeep
Normal file
0
tools/.gitkeep
Normal file
@ -1,86 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var page = require('webpage').create();
|
||||
var args = require('system').args;
|
||||
var params = {};
|
||||
var regexp = /^([^=]+)=([^$]+)/;
|
||||
|
||||
args.forEach(function(arg) {
|
||||
var parts = arg.match(regexp);
|
||||
if (!parts) {
|
||||
return;
|
||||
}
|
||||
params[parts[1]] = parts[2];
|
||||
});
|
||||
|
||||
var usage = 'url=<url> png=<filename> width=<width> height=<height> renderKey=<key>';
|
||||
|
||||
if (!params.url || !params.png || !params.renderKey || !params.domain) {
|
||||
console.log(usage);
|
||||
phantom.exit();
|
||||
}
|
||||
|
||||
phantom.addCookie({
|
||||
name: 'renderKey',
|
||||
value: params.renderKey,
|
||||
domain: params.domain,
|
||||
});
|
||||
|
||||
page.viewportSize = {
|
||||
width: params.width || '800',
|
||||
height: params.height || '400',
|
||||
};
|
||||
|
||||
var timeoutMs = (parseInt(params.timeout) || 10) * 1000;
|
||||
var waitBetweenReadyCheckMs = 50;
|
||||
var totalWaitMs = 0;
|
||||
|
||||
page.open(params.url, function(status) {
|
||||
console.log('Loading a web page: ' + params.url + ' status: ' + status, timeoutMs);
|
||||
|
||||
page.onError = function(msg, trace) {
|
||||
var msgStack = ['ERROR: ' + msg];
|
||||
if (trace && trace.length) {
|
||||
msgStack.push('TRACE:');
|
||||
trace.forEach(function(t) {
|
||||
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : ''));
|
||||
});
|
||||
}
|
||||
console.error(msgStack.join('\n'));
|
||||
};
|
||||
|
||||
function checkIsReady() {
|
||||
var panelsRendered = page.evaluate(function() {
|
||||
var panelCount = document.querySelectorAll('plugin-component').length;
|
||||
return window.panelsRendered >= panelCount;
|
||||
});
|
||||
|
||||
if (panelsRendered || totalWaitMs > timeoutMs) {
|
||||
var bb = page.evaluate(function() {
|
||||
var container = document.getElementsByClassName('dashboard-container');
|
||||
if (container.length == 0) {
|
||||
container = document.getElementsByClassName('panel-container');
|
||||
}
|
||||
return container[0].getBoundingClientRect();
|
||||
});
|
||||
|
||||
// reset viewport to render full page
|
||||
page.viewportSize = {
|
||||
width: bb.width,
|
||||
height: bb.height,
|
||||
};
|
||||
|
||||
setTimeout(function() {
|
||||
page.render(params.png);
|
||||
phantom.exit();
|
||||
}, 5);
|
||||
} else {
|
||||
totalWaitMs += waitBetweenReadyCheckMs;
|
||||
setTimeout(checkIsReady, waitBetweenReadyCheckMs);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(checkIsReady, waitBetweenReadyCheckMs);
|
||||
});
|
||||
})();
|
50
yarn.lock
50
yarn.lock
@ -11923,7 +11923,7 @@ extglob@^2.0.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
extract-zip@1.6.7, extract-zip@^1.6.5, extract-zip@^1.6.6:
|
||||
extract-zip@1.6.7, extract-zip@^1.6.6:
|
||||
version "1.6.7"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
|
||||
integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
|
||||
@ -12611,15 +12611,6 @@ fs-extra@^0.30.0:
|
||||
path-is-absolute "^1.0.0"
|
||||
rimraf "^2.2.8"
|
||||
|
||||
fs-extra@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950"
|
||||
integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^2.1.0"
|
||||
klaw "^1.0.0"
|
||||
|
||||
fs-extra@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
|
||||
@ -13567,14 +13558,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
hasha@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1"
|
||||
integrity sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=
|
||||
dependencies:
|
||||
is-stream "^1.0.1"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
hast-to-hyperscript@^7.0.0:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-7.0.3.tgz#e36b2a32b237f83bbb80165351398226f12b7d6e"
|
||||
@ -15752,11 +15735,6 @@ just-extend@^4.0.2:
|
||||
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc"
|
||||
integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==
|
||||
|
||||
kew@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
|
||||
integrity sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
@ -18605,21 +18583,6 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
phantomjs-prebuilt@2.1.16:
|
||||
version "2.1.16"
|
||||
resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz#efd212a4a3966d3647684ea8ba788549be2aefef"
|
||||
integrity sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=
|
||||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
extract-zip "^1.6.5"
|
||||
fs-extra "^1.0.0"
|
||||
hasha "^2.2.0"
|
||||
kew "^0.7.0"
|
||||
progress "^1.1.8"
|
||||
request "^2.81.0"
|
||||
request-progress "^2.0.1"
|
||||
which "^1.2.10"
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.0.7:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
|
||||
@ -21577,13 +21540,6 @@ request-progress@3.0.0:
|
||||
dependencies:
|
||||
throttleit "^1.0.0"
|
||||
|
||||
request-progress@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08"
|
||||
integrity sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=
|
||||
dependencies:
|
||||
throttleit "^1.0.0"
|
||||
|
||||
request-promise-core@1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346"
|
||||
@ -21600,7 +21556,7 @@ request-promise-native@^1.0.5:
|
||||
stealthy-require "^1.1.1"
|
||||
tough-cookie "^2.3.3"
|
||||
|
||||
request@2.88.0, "request@>=2.76.0 <3.0.0", request@^2.55.0, request@^2.81.0, request@^2.83.0, request@^2.87.0, request@^2.88.0:
|
||||
request@2.88.0, "request@>=2.76.0 <3.0.0", request@^2.55.0, request@^2.83.0, request@^2.87.0, request@^2.88.0:
|
||||
version "2.88.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
|
||||
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
|
||||
@ -25300,7 +25256,7 @@ which-pm-runs@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
|
||||
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
|
||||
|
||||
which@1, which@1.3.1, which@^1.2.10, which@^1.2.14, which@^1.2.4, which@^1.2.9, which@^1.3.0, which@^1.3.1, which@~1.3.0:
|
||||
which@1, which@1.3.1, which@^1.2.14, which@^1.2.4, which@^1.2.9, which@^1.3.0, which@^1.3.1, which@~1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
||||
|
Loading…
Reference in New Issue
Block a user