Compare commits

...

31 Commits

Author SHA1 Message Date
Kevin Minehart
9b8a50cc21 [v11.0.x] CI: Support more version formats in publishing (#94747)
CI: Support more version formats in publishing (#94575)

* cleanup dead code
* add tests and rewrite publish grafanacom steps to reuse
* add pkg/build tests; don't upload CDN assets on grafana releases

(cherry picked from commit 7a2edd35d5)
2024-10-15 14:53:19 -05:00
Jev Forsberg
5e53e8d177 [v11.0.x] CI: Bump alpine version (#93873)
CI: Bump alpine version (#93865)

* baldm0mma/up_alpine/ update alpine

* baldm0mma/resolve commits

(cherry picked from commit dc1670ed9a)
2024-10-02 13:41:14 +01:00
joshhunt
dfaad692c5 Update golang to 1.22.7 2024-10-02 13:35:36 +01:00
joshhunt
62f6977a22 Create apps directory for https://github.com/grafana/grafana-build/pull/359 2024-10-02 13:33:23 +01:00
Kevin Minehart
f2a39a5d26 [v11.0.x] CI: upgrade grabpl v3.0.53 (#94123)
CI: upgrade grabpl v3.0.53 (#94112)

* upgrade grabpl to v3.0.53

* upgrade grabpl to v3.0.53

(cherry picked from commit 26c3ed89a3)
2024-10-02 13:30:05 +01:00
github-actions[bot]
70316b3e14 apply security patch: v11.0.x/184-202409301526.patch
commit 52978144184ecdfea4fca8716c8aa194f42bf965
Author: Scott Lepper <scott.lepper@gmail.com>
Date:   Mon Sep 30 11:24:19 2024 -0400

    remove sql expressions 11.0.x
2024-10-02 11:28:15 +01:00
github-actions[bot]
0421a8911c apply security patch: v11.0.x/0001-Alerting-Fix-permission-on-external-rule-write-endpo.patch
commit a3d11a26e94faa3fbf04cf4adf9bf2bf5727bf20
Author: Alex Weaver <weaver.alex.d@gmail.com>
Date:   Thu Aug 22 12:04:09 2024 -0500

    Alerting: Fix permission on external rule write endpoint
2024-09-06 14:09:26 +00:00
Jev Forsberg
078bfbff5d [v11.0.x] CI/CD: Update retry logic for package validation (#92992)
CI/CD: Update retry logic for package validation (#92943)

* baldm0mma/retry_command/ update retry logic for package validation

* baldm0mma/retry_command/ update to 15 minutes

* baldm0mma/retry_command/ update to 30 minute retry

* baldm0mma/retry_command/ make drone

(cherry picked from commit d3ceaf41c2)
2024-09-05 09:56:37 -06:00
grafana-delivery-bot[bot]
53f1fc1a8c [v11.0.x] CI: Ensure changelogs are prettified prior to commit (#92914)
CI: Ensure changelogs are prettified prior to commit (#92580)

* Ensure changelogs are prettified prior to commit

* Remove cache property

* Include .yarn directory

* Include packages directory

* Try just using npx

* Update workflows

(cherry picked from commit 8daa6f1f30)

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>
2024-09-04 12:16:28 +01:00
grafana-delivery-bot[bot]
75211bfede [v11.0.x] Alerting docs: adds caps not supported in email contact points (#92910)
Alerting docs: adds caps not supported in email contact points (#92902)

* Alerting docs: adds caps not supported in email contact points

* ran prettier

* feedback from antonio

* ran prettier

(cherry picked from commit 9d3d1703f7)

Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>
2024-09-04 12:12:59 +02:00
grafana-delivery-bot[bot]
881fc7d38a [v11.0.x] alerting: docs / highlight tutorial in landing page (#92822)
alerting: docs / highlight tutorial in landing page (#92805)

(cherry picked from commit 0df5a6aefc)

Co-authored-by: antonio <45235678+tonypowa@users.noreply.github.com>
2024-09-02 17:01:12 +02:00
grafana-delivery-bot[bot]
a797dbc1a8 [v11.0.x] docs: Canvas updates adding why and how describing data needs (#92781)
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-30 13:25:52 -04:00
grafana-delivery-bot[bot]
82ab0be934 [v11.0.x] docs: Histogram update added missing options (#92777)
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-30 13:24:41 -04:00
grafana-delivery-bot[bot]
07bec6cfbe [v11.0.x] docs: Alert list update adding missing elements and descriptions. (#92773)
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-30 13:17:08 -04:00
grafana-delivery-bot[bot]
d59faa1aed [v11.0.x] docs: updates to Pie chart documentation (#92769)
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-30 11:22:40 -04:00
grafana-delivery-bot[bot]
467ffbd9fb [v11.0.x] docs: StatusHistory update added missing option and reordered headers (#92756)
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-30 09:35:47 -04:00
Isabel Matwawana
f801f70db4 [v11.0.x] docs: Added geomap missing headings, why and how describing data needs (#92710)
Co-authored-by: Drew Slobodnjak <60050885+drew08t@users.noreply.github.com>
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-29 15:28:13 -04:00
Isabel Matwawana
66f5eee27a [v11.0.x] docs: added explanation of why to use tables and how to use with their data needs (#92708)
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-29 15:27:30 -04:00
grafana-delivery-bot[bot]
a4611c12fa [v11.0.x] docs: Bar gauge doc updates adding why and how, explaining data needs (#92682)
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-29 10:41:11 -04:00
grafana-delivery-bot[bot]
603a713be3 [v11.0.x] docs: Barchart doc updates add video, why, how and data needs (#92677)
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-29 10:38:47 -04:00
grafana-delivery-bot[bot]
4e70aabcab [v11.0.x] Remove all documented references to time_options (#92662)
Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
2024-08-29 13:53:10 +01:00
grafana-delivery-bot[bot]
5898c359ee [v11.0.x] TutorialCard: Fix link to tutorial not opening (#92645) 2024-08-29 12:13:48 +02:00
grafana-delivery-bot[bot]
e31ff0175b [v11.0.x] Provisioning: Prevent provisioning folder errors from failing startup (#92588)
Provisioning: Prevent provisioning folder errors from failing startup (#92560)

* Prevent provisioning folder errors from failing startup
* Refactor setting of dashboard provisioner

(cherry picked from commit 34149c86d0)

Co-authored-by: Arati R. <33031346+suntala@users.noreply.github.com>
2024-08-28 15:40:23 +02:00
grafana-delivery-bot[bot]
b690b4f245 [v11.0.x] docs: Nodegraph doc updates adding why and how explaining data needs (#92538)
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-27 17:36:10 -04:00
grafana-delivery-bot[bot]
7b35fccf07 [v11.0.x] CI: Run prettier in bump-version workflow (#92529)
CI: Run prettier in bump-version workflow (#92195)

Run prettier

(cherry picked from commit 931d64b8fc)

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>
2024-08-27 22:39:59 +03:00
grafana-delivery-bot[bot]
fae2a7597f [v11.0.x] docs: Update logs doc adding why and how to including data reqs (#92522)
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-27 14:29:40 -04:00
Jack Baldry
ffa3ee4a44 [v11.0.x] Reorganize Explore Metrics content so OSS TOC matches Cloud (#92502) 2024-08-27 17:03:14 +01:00
github-actions[bot]
926256d53b Release: 11.0.4 (#92481)
* Update changelog

* Update version to 11.0.4

* Lint

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Andreas Christou <andreas.christou@grafana.com>
2024-08-27 14:22:02 +01:00
grafana-delivery-bot[bot]
df3ca0a686 [v11.0.x] Update make docs procedure (#92459)
Co-authored-by: grafanabot <bot@grafana.com>
Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-27 09:09:28 +01:00
grafana-delivery-bot[bot]
36d78a12b9 [v11.0.x] docs: time series add why use it and how to data needs (#92446)
Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
Co-authored-by: Señor Performo - Leandro Melendez <54183040+srperf@users.noreply.github.com>
2024-08-26 17:32:07 -04:00
grafana-delivery-bot[bot]
5087a32caf [v11.0.x] Chore: Update swagger ui (4.3.0 to 5.17.14) (#92345)
Chore: Update swagger ui (4.3.0 to 5.17.14) (#92341)

(cherry picked from commit 1256172df5)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2024-08-23 10:59:33 +03:00
156 changed files with 2103 additions and 5542 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -66,8 +66,15 @@ jobs:
sparse-checkout: |
.github/workflows
CHANGELOG.md
.nvmrc
.prettierignore
.prettierrc.js
fetch-depth: 0
fetch-tags: true
- name: Setup nodejs environment
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: "Configure git user"
run: |
git config --local user.name "github-actions[bot]"
@@ -111,9 +118,11 @@ jobs:
fi
git diff CHANGELOG.md
git add CHANGELOG.md
- name: "Prettify CHANGELOG.md"
run: npx prettier --write CHANGELOG.md
- name: "Commit changelog changes"
run: git commit --allow-empty -m "Update changelog" CHANGELOG.md
run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md
- name: "git push"
if: ${{ inputs.dry_run }} != true
run: git push

View File

@@ -69,6 +69,10 @@ jobs:
fetch-depth: '0'
fetch-tags: 'false'
path: .grafana-main
- name: Setup nodejs environment
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- name: Configure git user
run: |
git config --local user.name "github-actions[bot]"
@@ -115,7 +119,9 @@ jobs:
rm -f CHANGELOG.part changelog_items.md
git diff CHANGELOG.md
- name: "Prettify CHANGELOG.md"
run: npx prettier --write CHANGELOG.md
- name: Commit CHANGELOG.md changes
run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md

View File

@@ -1,3 +1,15 @@
<!-- 11.0.4 START -->
# 11.0.4 (2024-08-27)
### Bug fixes
- **Alerting:** Fix persisting result fingerprint that is used by recovery threshold [#91328](https://github.com/grafana/grafana/pull/91328), [@yuri-tceretian](https://github.com/yuri-tceretian)
- **Auditing:** Fix a possible crash when audit logger parses responses for failed requests (Enterprise)
- **RBAC:** Fix seeder failures when inserting duplicated permissions (Enterprise)
- **Snapshots:** Fix panic when snapshot_remove_expired is true [#91330](https://github.com/grafana/grafana/pull/91330), [@ryantxu](https://github.com/ryantxu)
<!-- 11.0.4 END -->
<!-- 11.0.3 START -->
# 11.0.3 (2024-08-14)

0
apps/.gitkeep Normal file
View File

View File

@@ -6,6 +6,15 @@
# [Semantic versioning](https://semver.org/) is used to help the reader identify the significance of changes.
# Changes are relevant to this script and the support docs.mk GNU Make interface.
#
# ## 8.1.0 (2024-08-22)
#
# ### Added
#
# - Additional website mounts for projects that use the website repository.
#
# Mounts are required for `make docs` to work in the website repository or with the website project.
# The Makefile is also mounted for convenient development of the procedure that repository.
#
# ## 8.0.1 (2024-07-01)
#
# ### Fixed
@@ -727,6 +736,9 @@ POSIX_HERESTRING
_repo="$(repo_path website)"
volumes="--volume=${_repo}/config:/hugo/config:z"
volumes="${volumes} --volume=${_repo}/content/guides:/hugo/content/guides:z"
volumes="${volumes} --volume=${_repo}/content/whats-new:/hugo/content/whats-new:z"
volumes="${volumes} --volume=${_repo}/Makefile:/hugo/Makefile:z"
volumes="${volumes} --volume=${_repo}/layouts:/hugo/layouts:z"
volumes="${volumes} --volume=${_repo}/scripts:/hugo/scripts:z"
fi

View File

@@ -75,6 +75,9 @@ To set up email integration, complete the following steps.
1. Enter a contact point name.
1. From the Integration list, select **Email**.
1. Enter the email addresses you want to send notifications to.
E-mail addresses are case sensitive. Ensure that the e-mail address entered is correct.
1. Click **Test** to check that your integration works.
1. Click **Save contact point**.

View File

@@ -57,7 +57,11 @@ refs:
# Introduction to Alerting
Whether youre just starting out or you're a more experienced user of Grafana Alerting, learn more about the fundamentals and available features that help you create, manage, and respond to alerts; and improve your teams ability to resolve issues quickly. For a hands-on introduction, refer to our [tutorial to get started with Grafana Alerting](http://grafana.com/tutorials/alerting-get-started/).
Whether youre just starting out or you're a more experienced user of Grafana Alerting, learn more about the fundamentals and available features that help you create, manage, and respond to alerts; and improve your teams ability to resolve issues quickly.
{{< admonition type="tip" >}}
For a hands-on introduction, refer to our [tutorial to get started with Grafana Alerting](http://grafana.com/tutorials/alerting-get-started/).
{{< /admonition >}}
The following diagram gives you an overview of Grafana Alerting and introduces you to some of the fundamental features that are the principles of how Grafana Alerting works.

View File

@@ -57,7 +57,6 @@ In the following JSON, id is shown as null which is the default value assigned t
"to": "now"
},
"timepicker": {
"time_options": [],
"refresh_intervals": []
},
"templating": {
@@ -136,17 +135,6 @@ The grid has a negative gravity that moves panels up if there is empty space abo
"now": true,
"hidden": false,
"nowDelay": "",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
@@ -174,7 +162,6 @@ Usage of the fields is explained below:
| **now** | |
| **hidden** | whether timepicker is hidden or not |
| **nowDelay** | override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. |
| **time_options** | options available in the time picker dropdown |
| **refresh_intervals** | interval options available in the refresh picker dropdown |
| **status** | |
| **type** | |

View File

@@ -217,31 +217,7 @@ Content-Length: 1300
Content-Length: 46
```
JSON response body schema:
- **message** - Message explaining the reason for the request failure.
## Restore dashboard by dashboard UID
`POST /api/dashboards/uid/:uid/restore`
Restores a dashboard to a given dashboard version using `uid`.
**Example request for restoring a dashboard version**:
```http
POST /api/dashboards/uid/QA7wKklGz/restore
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"version": 1
}
```
JSON body schema:
JSON response body schema:
- **message** - Message explaining the reason for the request failure.
@@ -328,31 +304,7 @@ Content-Length: 1300
**Example response (JSON diff)**:
Status Codes:
- **200** - OK
- **400** - Bad request (invalid JSON sent)
- **401** - Unauthorized
- **404** - Not found
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
```http
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8

View File

@@ -0,0 +1,18 @@
---
description: Use your telemetry data to explore and determine the root cause of issues without performing queries.
keywords:
- Simplified exploration
- queryless
- Explore apps
title: Simplified exploration
menuTitle: Simplified exploration
weight: 100
---
# Simplified exploration
Introducing the Grafana Explore apps, designed for effortless data exploration through intuitive, queryless interactions.
Easily explore telemetry signals with these specialized tools, tailored specifically for the Grafana databases to provide quick and accurate insights.
{{< section >}}

View File

@@ -6,7 +6,9 @@ labels:
- oss
title: Explore Metrics
aliases:
description: This topic describes the Explore Metrics feature
- ../explore-metrics/ # /docs/grafana/latest/explore/explore-metrics/
canonical: https://grafana.com/docs/grafana/latest/explore/simplified-exploration/metrics/
description: Explore Metrics lets you browse Prometheus-compatible metrics using an intuitive, queryless experience.
weight: 200
---

View File

@@ -50,6 +50,10 @@ Once youve created a [dashboard](https://grafana.com/docs/grafana/<GRAFANA_VE
Use the following options to refine your alert list visualization.
### View mode
Choose between **List** to display alerts in a detailed list format with comprehensive information, or **Stat** to show alerts as a summarized single-value statistic.
### Group mode
Choose between **Default grouping** to show alert instances grouped by their alert rule, or **Custom grouping** to show alert instances grouped by a custom set of labels.
@@ -103,8 +107,8 @@ Filter alerts by the selected folder. Only alerts from dashboards in this folder
Choose which alert states to display in this visualization.
- Alerting / Firing
- Pending
- No Data
- Normal
- Error
- **Alerting / Firing -** Shows alerts that are currently active and triggering an alert condition.
- **Pending -** Shows alerts that are in a transitional state, waiting for conditions to be met before triggering.
- **No Data -** Shows alerts where the data source is not returning any data, which could indicate an issue with data collection.
- **Normal -** Shows alerts that are in a normal or resolved state, where no alert condition is currently met.
- **Error -** Shows alerts where an error has occurred, typically related to an issue in the alerting process.

View File

@@ -41,31 +41,57 @@ refs:
# Bar chart
Bar charts allow you to graph categorical data.
A bar chart is a visual representation that uses rectangular bars, where the length of each bar represents each value.
You can use the bar chart visualization when you want to compare values over different categories or time periods. The visualization can display the bars horizontally or vertically, and can be customized to group or stack bars for more complex data analysis.
{{< figure src="/static/img/docs/bar-chart-panel/barchart_small_example.png" max-width="1000px" caption="Bar chart" >}}
{{< figure src="/static/img/docs/bar-chart-panel/barchart_small_example.png" max-width="1000px" alt="Bar chart" >}}
You can use the bar chart visualization if you need to show:
- Population distribution by age or location
- CPU usage per application
- Sales per division
- Server cost distribution
## Configure a bar chart
The following video shows you how to create and configure a bar chart visualization:
{{< youtube id="qyKE9-71KkE" >}}
{{< docs/play title="Grafana Bar Charts and Pie Charts" url="https://play.grafana.org/d/ktMs4D6Mk/" >}}
## Supported data formats
Only one data frame is supported and it must have at least one string field that will be used as the category for an X or Y axis and one or more numerical fields.
To create a bar chart visualization, you need a dataset containing one string or time field (or column) and at least one numeric field, though preferably more than one to make best use of the visualization.
Example:
The text or time field is used to label the bars or values in each row of data and the numeric fields are represented by proportionally sized bars.
| Browser | Market share |
| ------- | ------------ |
| Chrome | 50 |
| IE | 17.5 |
### Example 1
If you have more than one numerical field the visualization will show grouped bars.
| Group | Value1 | Value2 | Value3 |
| ----- | ------ | ------ | ------ |
| uno | 5 | 3 | 2 |
### Visualizing time series or multiple result sets
![Bar chart single row example](/media/docs/grafana/panels-visualizations/screenshot-grafana-11.1-barchart-example1.png 'Bar chart single row example')
If you have multiple time series or tables you first need to join them using a join or reduce transform. For example if you
have multiple time series and you want to compare their last and max value add the **Reduce** transform and specify **Max** and **Last** as options under **Calculations**.
If you have more than one text or time field, by default, the visualization uses the first one, but you can change this in the x-axis option as described in the [Bar chart options](#bar-chart-options) section.
{{< figure src="/static/img/docs/bar-chart-panel/bar-chart-time-series-v8-0.png" max-width="1025px" caption="Bar chart time series example" >}}
### Example 2
If your dataset contains multiple rows, the visualization displays multiple bar chart groups where each group contains multiple bars representing all the numeric values for a row.
| Group | Value1 | Value2 | Value3 |
| ----- | ------ | ------ | ------ |
| uno | 5 | 3 | 2 |
| dos | 10 | 6 | 4 |
| tres | 20 | 8 | 2 |
![Bar chart multiple row example](/media/docs/grafana/panels-visualizations/screenshot-grafana-11.1-barchart-example2.png 'Bar chart multiple row example')
While the first field can be time-based and you can use a bar chart to plot time-series data, for large amounts of time-series data, we recommend that you use the [time series visualization](https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/time-series/) and configure it to be displayed as bars.
We recommend that you only use one dataset in a bar chart because using multiple datasets can result in unexpected behavior.
## Panel options
@@ -75,6 +101,10 @@ have multiple time series and you want to compare their last and max value add t
Use these options to refine your visualization.
### X Axis
Specify which field is used for the x-axis.
### Orientation
- **Auto** - Grafana decides the bar orientation based on what the panel dimensions.

View File

@@ -25,14 +25,70 @@ refs:
# Bar gauge
Bar gauges simplify your data by reducing every field to a single value. You choose how Grafana calculates the reduction.
This panel can show one or more bar gauges depending on how many series, rows, or columns your query returns.
Bar gauges simplify your data by reducing every field to a single value. You choose how Grafana calculates the reduction. This visualization can show one or more bar gauges depending on how many series, rows, or columns your query returns.
{{< figure src="/static/img/docs/v66/bar_gauge_cover.png" max-width="1025px" alt="Bar gauge panel" >}}
The bar gauge visualization displays values as bars with various lengths or fills proportional to the values they represent. They differ from traditional bar charts in that they act as gauges displaying metrics between ranges. One example is a thermometer displaying body temperature in a bar filling up.
You can use a bar gauge visualization when you need to show:
- Key performance indicators (KPIs)
- System health
- Savings goals
- Attendance
- Process completion rates
{{< docs/play title="Bar Gauge" url="https://play.grafana.org/d/vmie2cmWz/" >}}
## Supported data formats
To create a bar gauge visualization, you need a dataset querying at least one numeric field. Every numeric field in the dataset is displayed as a bar gauge. Text or time fields aren't required but if they're present, they're used for labeling.
### Example 1
| Label | Value1 | Value2 | Value3 |
| ----- | ------ | ------ | ------ |
| Row1 | 5 | 3 | 2 |
![Bar gauge with single row of data](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-bargauge-example1.png)
The minimum and maximum range for the bar gauges is automatically pulled from the largest and smallest numeric values in the dataset. You can also manually define the minimum and maximum values as indicated in the [Standard options](#standard-options) section.
You can also define the minimum and maximum from the dataset provided.
### Example 2
| Label | Value | Max | Min |
| ----- | ----- | --- | --- |
| Row1 | 3 | 6 | 1 |
![Bar gauge with single row of data including maximum and minimum](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-bargauge-example2.png)
If you dont want to show gauges for the min and max values, you can configure only one field to be displayed as described in the [Value options](#value-options) section.
![Bar gauge, single row of data with max and min displaying value](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-bargauge-example3.png)
Even if the min and max arent displayed, the visualization still pulls the range from the data set.
### Example 3
The bar gauge visualization also supports multiple records (rows) in the dataset.
| Label | Value1 | Value2 | Value3 |
| ----- | ------ | ------ | ------ |
| Row1 | 5 | 3 | 2 |
| Row2 | 10 | 6 | 4 |
| Row3 | 20 | 8 | 2 |
![Bar gauge with multiple rows of data displaying last row](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-bargauge-example4.png)
By default, the visualization is configured to [calculate](#value-options) a single value per column or series and to display only the last set of data. However, it derives the minimum and maximum from the full dataset even if those values arent visible. In this example, that means only the last row of data is displayed in the gauges and the minimum and maximum values are defined as 2 and 20, pulled from the whole dataset.
If you want to show one gauge per cell you can change the [Show](#show) setting from [Calculate](#calculate) to [All values](#all-values) and each bar is labeled by concatenating the text column with each value's column name.
![Bar gauge with multiple rows of data displaying all the values](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-bargauge-example5.png)
## Panel options
{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
@@ -156,6 +212,10 @@ This option only applies when bar size is set to manual.
{{< docs/shared lookup="visualizations/thresholds-options-2.md" source="grafana" version="<GRAFANA_VERSION>" >}}
Last, colors of the bar gauge thresholds can be configured as described above.
![Bar gauge with colored thresholds configured](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-bargauge-example6.png)
## Field overrides
{{< docs/shared lookup="visualizations/overrides-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}

View File

@@ -30,11 +30,26 @@ refs:
# Canvas
Canvases combine the power of Grafana with the flexibility of custom elements. Canvases are extensible form-built visualizations that allow you to explicitly place elements within static and dynamic layouts. This empowers you to design custom visualizations and overlay data in ways that aren't possible with standard Grafana panels, all within Grafana's UI. If you've used popular UI and web design tools, then designing canvases will feel very familiar.
Canvases combine the power of Grafana with the flexibility of custom elements.
They are extensible visualizations that allow you to add and arrange elements wherever you want within unstructured static and dynamic layouts.
This lets you design custom visualizations and overlay data in ways that aren't possible with standard Grafana visualizations, all within the Grafana UI.
> We would love your feedback on Canvas. Please check out the [open Github issues](https://github.com/grafana/grafana/issues?page=1&q=is%3Aopen+is%3Aissue+label%3Aarea%2Fpanel%2Fcanvas) and [submit a new feature request](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request,area%2Fpanel%2Fcanvas&title=Canvas:&projects=grafana-dataviz&template=1-feature_requests.md) as needed.
{{< video-embed src="/static/img/docs/canvas-panel/canvas-beta-overview-9-2-0.mp4" max-width="750px" alt="Canvas beta overview" >}}
{{< video-embed src="/static/img/docs/canvas-panel/canvas-beta-overview-9-2-0.mp4" max-width="750px" caption="Canvas beta overview" >}}
If you've used popular UI and web design tools, then designing canvases will feel very familiar.
With all of these dynamic elements, there's almost no limit to what a canvas can display.
{{< admonition type="note" >}}
We'd love your feedback on the canvas visualization. Please check out the [open Github issues](https://github.com/grafana/grafana/issues?page=1&q=is%3Aopen+is%3Aissue+label%3Aarea%2Fpanel%2Fcanvas) and [submit a new feature request](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request,area%2Fpanel%2Fcanvas&title=Canvas:&projects=grafana-dataviz&template=1-feature_requests.md) as needed.
{{< /admonition >}}
## Supported data formats
The canvas visualization is unique in that it doesn't have any specific data requirements. You can even start adding and configuring visual elements without providing any data. However, any data you plan to consume should be accessible through supported Grafana data sources and structured in a way that ensures smooth integration with your custom elements.
If your canvas is going to update in real time, your data should support refreshes at your desired intervals without degrading the user experience.
You can tie [Elements](#elements) and [Connections](#connections) to data through options like text, colors, and background pattern images, etc. available in the canvas visualization.
## Elements

View File

@@ -49,11 +49,28 @@ refs:
# Geomap
Geomaps allow you to view and customize the world map using geospatial data. You can configure various overlay styles and map view settings to easily focus on the important location-based characteristics of the data.
Geomaps allow you to view and customize the world map using geospatial data. It's the ideal visualization if you have data that includes location information and you want to see it displayed in a map.
> We would love your feedback on geomaps. Please check out the [open Github issues](https://github.com/grafana/grafana/issues?page=1&q=is%3Aopen+is%3Aissue+label%3Aarea%2Fpanel%2Fgeomap) and [submit a new feature request](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request,area%2Fpanel%2Fgeomap&title=Geomap:&projects=grafana-dataviz&template=1-feature_requests.md) as needed.
You can configure and overlay [map layers](#types), like heatmaps and networks, and blend included basemaps or your own custom maps. This helps you to easily focus on the important location-based characteristics of the data.
{{< figure src="/static/img/docs/geomap-panel/geomap-example-8-1-0.png" max-width="1200px" caption="Geomap panel" >}}
{{< figure src="/static/img/docs/geomap-panel/geomap-example-8-1-0.png" max-width="1200px" alt="Geomap visualization" >}}
When a geomap is in focus, in addition to typical mouse controls, you can pan around using the arrow keys or zoom in and out using the plus (`+`) and minus (`-`) keys or icons.
Geomaps are also useful when you have location data thats changing in real time and you want to visualize where an element is moving, using auto-refresh.
You can use a geomap visualization if you need to:
- Track your fleet of vehicles and associated metrics
- Show the locations and statuses of data centers or other connected assets in a network
- Display geographic trends in a heatmap
- Visualize the relationship of your locations' HVAC consumption or solar production with the sun's location
{{< admonition type="note" >}}
We'd love your feedback on the geomap visualization. Please check out the [open Github issues](https://github.com/grafana/grafana/issues?page=1&q=is%3Aopen+is%3Aissue+label%3Aarea%2Fpanel%2Fgeomap) and [submit a new feature request](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request,area%2Fpanel%2Fgeomap&title=Geomap:&projects=grafana-dataviz&template=1-feature_requests.md) as needed.
{{< /admonition >}}
## Configure a geomap visualization
The following video provides beginner steps for creating geomap visualizations. You'll learn the data requirements and caveats, special customizations, preconfigured displays and much more:
@@ -61,6 +78,69 @@ The following video provides beginner steps for creating geomap visualizations.
{{< docs/play title="Geomap Examples" url="https://play.grafana.org/d/panel-geomap/" >}}
## Supported data formats
To create a geomap visualization, you need datasets containing fields with location information.
The supported location formats are:
- Latitude and longitude
- Geohash
- Lookup codes: country, US states, or airports
To learn more, refer to [Location mode](#location-mode).
Geomaps also support additional fields with various data types to define things like labels, numbers, heat sizes, and colors.
### Example - Latitude and longitude
If you plan to use latitude and longitude coordinates, the dataset must include at least two fields (or columns): one called `latitude` (you can also use`lat`), and one called `longitude` (also `lon` or `lng`). When you use this naming convention, the visualization automatically detects the fields and displays the elements. The order of the fields doesn't matter as long as there is one latitude and one longitude.
| Name | latitude | longitude | value |
| --------------- | --------- | --------- | ----- |
| Disneyland | 33.8121 | -117.9190 | 4 |
| DisneyWorld | 28.3772 | -81.5707 | 10 |
| EuroDisney | 48.867374 | 2.784018 | 3 |
| Tokyo Disney | 35.6329 | 139.8804 | 70 |
| Shanghai Disney | 31.1414 | 121.6682 | 1 |
If your latitude and longitude fields are named differently, you can specify them, as indicated in the [Location mode](#location-mode) section.
### Example - Geohash
If your location data is in geohash format, the visualization requires at least one field (or column) containing location data.
If the field is named `geohash`, the visualization automatically detects the location and displays the elements. The order of the fields doesn't matter and the data set can have multiple other numeric, text, and time fields.
| Name | geohash | trips |
| --------- | ------------ | ----- |
| Cancun | d5f21 | 8 |
| Honolulu | 87z9ps | 0 |
| Palm Cove | rhzxudynb014 | 1 |
| Mykonos | swdj02ey9gyx | 3 |
If your field containing geohash location data is not named as above, you can configure the visualization to use geohash and specify which field to use, as explained in the [Location mode](#location-mode) section.
### Example - Lookup codes
The geomap visualization can identify locations based on country, airport, or US state codes.
For this configuration, the dataset must contain at least one field (or column) containing the location code.
If the field is named `lookup`, the visualization automatically detects it and displays points based on country codes.
| Year | lookup | gdp |
| ---- | ------ | --------- |
| 2016 | MEX | 104171935 |
| 2016 | DEU | 94393454 |
| 2016 | FRA | 83654250 |
| 2016 | BRA | 80921527 |
| 2016 | CAN | 79699762 |
The other location types&mdash; airport codes or US state codes&mdash;aren't automatically detected.
If you want to use other codes or give the field a custom name, you can follow the steps in the [Location mode](#location-mode) section.
## Panel options
{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}

View File

@@ -100,6 +100,10 @@ The data is converted as follows:
Use the following options to refine your histogram visualization.
### Bucket count
Specifies the number of bins used to group your data in the histogram, affecting the granularity of the displayed distribution. Leave this empty for automatic bucket count of 30.
### Bucket size
The size of the buckets. Leave this empty for automatic bucket sizing (~10% of the full range).
@@ -112,6 +116,14 @@ If the first bucket should not start at zero. A non-zero offset has the effect o
This will merge all series and fields into a combined histogram.
### Stacking
Controls how multiple series are displayed in the histogram. Choose from the following:
- **Off** - Series are not stacked, but instead shown side by side.
- **Normal** - Series are stacked on top of each other, showing cumulative values.
- **100%** - Series are stacked to fill 100% of the chart, showing the relative proportion of each series.
### Line width
Controls line width of the bars.
@@ -126,17 +138,12 @@ Set the mode of the gradient fill. Fill gradient is based on the line color. To
Gradient display is influenced by the **Fill opacity** setting.
#### None
Choose from the following:
No gradient fill. This is the default setting.
#### Opacity
Transparency of the gradient is calculated based on the values on the Y-axis. The opacity of the fill is increasing with the values on the Y-axis.
#### Hue
Gradient color is generated based on the hue of the line color.
- **None** - No gradient fill. This is the default setting.
- **Opacity** - Transparency of the gradient is calculated based on the values on the Y-axis. The opacity of the fill is increasing with the values on the Y-axis.
- **Hue** - Gradient color is generated based on the hue of the line color.
- **Scheme** - The selected [color palette](https://grafana.com/docs/grafana/latest/panels-visualizations/configure-standard-options/#color-scheme) is applied to the histogram bars.
## Standard options

View File

@@ -19,7 +19,7 @@ description: Configure options for Grafana's logs visualization
title: Logs
weight: 100
refs:
supported-log-levels-and-mappings-of-log-level-abbreviation-and-expressions:
log-levels:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/explore/logs-integration/#log-level
- pattern: /docs/grafana-cloud/
@@ -28,27 +28,51 @@ refs:
# Logs
The logs visualization shows log lines from data sources that support logs, such as Elastic, Influx, and Loki. Typically you would use this visualization next to a graph visualization to display the log output of a related process.
_Logs_ are structured records of events or messages generated by a system or application&mdash;that is, a series of text records with status updates from your system or app. They generally include timestamps, messages, and context information like the severity of the logged event.
The logs visualization displays these records from data sources that support logs, such as Elastic, Influx, and Loki. The logs visualization has colored indicators of log status, as well as collapsible log events that help you analyze the information generated.
{{< figure src="/static/img/docs/v64/logs-panel.png" max-width="1025px" alt="Logs panel" >}}
{{< docs/play title="Logs Panel" url="https://play.grafana.org/d/6NmftOxZz/" >}}
The logs visualization shows the result of queries that were entered in the Query tab. The results of multiple queries are merged and sorted by time. You can scroll inside the panel if the data source returns more lines than can be displayed at any one time.
Typically, you use logs with a graph visualization to display the log output of a related process. If you have an incident in your application or systems, such as a website disruption or code failure, you can use the logs visualization to help you figure out what went wrong, when, and even why.
To limit the number of lines rendered, you can use the **Max data points** setting in the **Query options**. If it is not set, then the data source will usually enforce a default limit.
## Configure a log visualization
The following video provides a walkthrough of creating a logs visualization. You'll also learn how to customize some settings and log visualization caveats:
{{< youtube id="jSSi_x-fD_8" >}}
## Supported data formats
The logs visualization works best with log-type datasets such as queries from data sources like Loki, Elastic, and InlfuxDB.
You can also build log-formatted data from other data sources as long as the first field is a time type followed by string, number, and time fields. The leading time field is used to sort and timestamp the logs and if the data contains other time-type fields, theyre included as elements of the logged record.
The second field is used as the log record title regardless of whether its a time, numeric, or string field. Usually the second field is a text field containing multiple string elements, but if the message level (or `lvl`) is present, the visualization uses the values in it to add colors to the record, as described in [Log levels integration](ref:log-levels).
Subsequent fields are collapsed inside of each log record and you can open them by clicking the expand (`>`) icon.
To limit the number of log lines rendered in the visualization, you can use the **Max data points** setting in the panel **Query options**. If that option isn't set, then the data source typically enforces its own default limit.
### Example
| Time | TitleMessage | Element1 | Element2 | Element3 |
| ------------------- | -------------------- | -------- | -------- | ------------------- |
| 2023-02-01 12:00:00 | title=Log1 lvl=info | 1 | server2 | 2023-02-01 11:00:00 |
| 2023-02-01 11:30:00 | title=Log1 lvl=error | 1 | server2 | 2023-02-01 11:00:00 |
| 2023-02-01 11:00:00 | title=Log1 lvl=trace | 1 | server2 | 2023-02-01 11:00:00 |
![Logs Example](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-logs-example.png 'Logs Example')
## Panel options
{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Log level
For logs where a **level** label is specified, we use the value of the label to determine the log level and update color accordingly. If the log doesn't have a level label specified, we try to find out if its content matches any of the supported expressions (see below for more information). The log level is always determined by the first match. In case Grafana is not able to determine a log level, it will be visualized with **unknown** log level. See [supported log levels and mappings of log level abbreviation and expressions](ref:supported-log-levels-and-mappings-of-log-level-abbreviation-and-expressions).
For logs where a **level** label is specified, we use the value of the label to determine the log level and update color accordingly. If the log doesn't have a level label specified, we try to find out if its content matches any of the supported expressions (see below for more information). The log level is always determined by the first match. In case Grafana is not able to determine a log level, it will be visualized with **unknown** log level. See [supported log levels and mappings of log level abbreviation and expressions](ref:log-levels).
## Log details

View File

@@ -21,10 +21,24 @@ weight: 100
# Node graph
Node graphs can visualize directed graphs or networks. They use a directed force layout to effectively position the nodes, so they can display complex infrastructure maps, hierarchies, or execution diagrams.
Node graphs are useful when you need to visualize elements that are related to each other. This is done by displaying circles&mdash;or _nodes_&mdash;for each element you want to visualize, connected by lines&mdash;or _edges_. The visualization uses a directed force layout that positions the nodes into a network of connected circles.
Node graphs display useful information about each node, as well as the relationships between them, allowing you to visualize complex infrastructure maps, hierarchies, or execution diagrams.
![Node graph visualization](/static/img/docs/node-graph/node-graph-8-0.png 'Node graph')
The appearance of nodes and edges can also be customized in several ways including color, borders, and line style.
You can use a node graph visualization if you need to show:
- Solution topologies
- Networks
- Infrastructure
- Organizational charts
- Critical path diagrams
- Family trees
- Mind maps
## Configure a node graph visualization
The following video provides beginner steps for creating node panel visualizations. You'll learn the data requirements and caveats, special customizations, and much more:
@@ -33,6 +47,38 @@ The following video provides beginner steps for creating node panel visualizatio
{{< docs/play title="Node graph panel" url="https://play.grafana.org/d/bdodfbi3d57uoe/" >}}
## Supported data formats
To create node graphs, you need two datasets: one containing the records for the displayed elements (nodes) and one dataset containing the records for the connections between those elements (edges).
### Nodes dataset
The nodes dataset must contain one alphanumeric ID field that gives each element a unique identifier. The visualization also accepts other options fields for titles, subtitles, main and secondary stats, arc information for how much of the circle border to paint, details, colors, icons, node size, and indicators for element highlighting. For more information and naming conventions for these fields, refer to the [Nodes data frame structure](#nodes-data-frame-structure) section.
#### Example
| id | title | subtitle | mainstat | secondarystat | color | icon | highlighted |
| ----- | ----- | -------- | -------- | ------------- | ----- | ---- | ----------- |
| node1 | PC | Windows | AMD | 16gbRAM | blue | | true |
| node2 | PC | Linux | Intel | 32gbRAM | green | eye | false |
| node3 | Mac | MacOS | M3 | 16gbRAM | gray | apps | false |
| node4 | Alone | SoLonely | JustHere | NotConnected | red | | false |
If the icon field contains a value, its displayed instead of the title and subtitle. For a list of of available icons, refer to [Icons Overview](https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview).
### Edges dataset
Similar to the nodes dataset, the edges dataset needs one unique ID field for each relationship, followed by two fields containing the source and the target nodes of the edge; that is, the nodes the edge connects. Other optional fields are main and secondary stats, context menu elements, line thickness, highlight indications, line colors, and configurations to turn the connection into a dashed line. For more information and naming conventions for these fields, refer to the [Edges data frame structure](#edges-data-frame-structure) section.
#### Example
| id | source | target | mainstat | seconddarystat | thickness | highlighted | color |
| ----- | ------ | ------ | -------- | -------------- | --------- | ----------- | ------ |
| edge1 | node1 | node2 | TheMain | TheSub | 3 | true | cyan |
| edge2 | node3 | node2 | Main2 | Sub2 | 1 | false | orange |
If a node lacks edge connections, its displayed on its own outside of the network.
## Panel options
{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}

View File

@@ -23,9 +23,18 @@ refs:
# Pie chart
{{< figure src="/static/img/docs/pie-chart-panel/pie-chart-example.png" max-width="1200px" lightbox="true" caption="Pie charts" >}}
A pie chart is a graph that displays data as segments of a circle proportional to the whole, making it look like a sliced pie. Each slice corresponds to a value or measurement.
Pie charts display reduced series, or values in a series, from one or more queries, as they relate to each other, in the form of slices of a pie. The arc length, area and central angle of a slice are all proportional to the slices value, as it relates to the sum of all values. This type of chart is best used when you want a quick comparison of a small set of values in an aesthetically pleasing form.
{{< figure src="/static/img/docs/pie-chart-panel/pie-chart-example.png" max-width="1200px" lightbox="true" alt="Pie charts" >}}
The pie chart visualization is ideal when you have data that adds up to a total and you want to show the proportion of each value compared to other slices, as well as to the whole of the pie.
You can use a pie chart if you need to compare:
- Browser share distribution in the market
- Incident causes per category
- Network traffic sources
- User demographics
## Configure a pie chart visualization
@@ -35,6 +44,60 @@ The following video guides you through the creation steps and common customizati
{{< docs/play title="Grafana Bar Charts and Pie Charts" url="https://play.grafana.org/d/ktMs4D6Mk/" >}}
## Supported data formats
The pie chart is different from other visualizations in that it will only display one pie, regardless of the number of datasets, fields, or records queried in it.
To create a pie chart visualization, you need a dataset containing a set of numeric values either in rows, columns, or both.
### Example - One row
The easiest way to provide data for a pie chart visualization is in a dataset with a single record (or row) containing the fields (or columns) that you want in the pie, as in the following example. The default settings of the pie chart visualization automatically display each column as a slice of the pie.
| Value1 | Value2 | Value3 | Optional |
| ------ | ------ | ------ | -------- |
| 5 | 3 | 2 | Sums10 |
![Pie chart visualization with multiple values in a single row](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-pie-example1.png)
### Example - Multiple rows
If you need to use numeric data that's in multiple rows, the default **Show** parameter of the visualization [Value options](#value-options) is set to **Calculate** and use data from the last row.
| Value | Label |
| ----- | ------ |
| 5 | Value1 |
| 3 | Value2 |
| 2 | Value3 |
![Pie chart visualization with multiple row values showing the last one](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-pie-example2.png)
By default, the visualization is configured to [calculate](#value-options) a single value per column or series and to display only the last row of data.
To allow values in multiple rows to be displayed, change the **Show** setting in the [Value options](#value-options) from **Calculate** to **All values**.
![Pie chart visualization with multiple row values showing all values](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-pie-example3.png)
### Example - Multiple rows and columns
If your dataset contains multiple rows and columns with numeric data, by default only the last row's values are summed.
| Value1 | Value2 | Value3 | Optional |
| ------ | ------ | ------ | -------- |
| 5 | 3 | 2 | Sums10 |
| 10 | 6 | 4 | Sums20 |
| 20 | 8 | 2 | Sums30 |
![Pie chart visualization with multiple rows and columns showing the last one](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-pie-example4.png)
If you want to display all the cells, change the **Show** setting in the [Value options](#value-options) from **Calculate** to **All values**. This also labels the elements by concatenating all the text fields (if you have any) with the column name.
![Pie chart visualization with multiple rows and columns showing the all values](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-pie-example5.png)
If you want to display only the values from a given field (or column), once the **Show** setting in the [Value options](#value-options) is set to **All values**, set the **Fields** option to the column you wish to sum in the display. The value labels are also concatenated as indicated before.
![Pie chart visualization with multiple rows and columns showing values from one column](/media/docs/grafana/panels-visualizations/screenshot-grafana-12.1-pie-example6.png)
## Panel options
{{< docs/shared lookup="visualizations/panel-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
@@ -96,10 +159,6 @@ The following example shows a pie chart with **Name** and **Percent** labels dis
![Pie chart labels](/static/img/docs/pie-chart-panel/pie-chart-labels-7-5.png)
## Standard options
{{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Tooltip options
{{< docs/shared lookup="visualizations/tooltip-options-1.md" source="grafana" version="<GRAFANA_VERSION>" >}}
@@ -137,6 +196,14 @@ Select values to display in the legend. You can select more than one.
- **Percent:** The percentage of the whole.
- **Value:** The raw numerical value.
## Standard options
{{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Data links
{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Value mappings
{{< docs/shared lookup="visualizations/value-mappings-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}

View File

@@ -107,6 +107,10 @@ Use these options to refine the visualization.
Controls whether values are rendered inside the value boxes. Auto will render values if there is sufficient space.
### Row height
Controls the height of boxes. 1 = maximum space and 0 = minimum space.
### Column width
Controls the width of boxes. 1 = maximum space and 0 = minimum space.
@@ -119,10 +123,6 @@ Controls line width of state regions.
Controls the opacity of state regions.
## Standard options
{{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Legend options
{{< docs/shared lookup="visualizations/legend-options-2.md" source="grafana" version="<GRAFANA_VERSION>" >}}
@@ -131,6 +131,10 @@ Controls the opacity of state regions.
{{< docs/shared lookup="visualizations/tooltip-options-1.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Standard options
{{< docs/shared lookup="visualizations/standard-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Data links
{{< docs/shared lookup="visualizations/datalink-options.md" source="grafana" version="<GRAFANA_VERSION>" >}}

View File

@@ -49,13 +49,41 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-standard-options/#color-scheme
- pattern: /docs/grafana-cloud
destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-standard-options/#color-scheme
field-override:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-overrides/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/panels-visualizations/configure-overrides/
data-transformation:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/transform-data/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/transform-data/
build-query:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/panels-visualizations/query-transform-data/
---
# Table
Tables are a highly flexible visualization designed to display data in columns and rows. They support various data types, including tables, time series, annotations, and raw JSON data. The table visualization can even take multiple data sets and provide the option to switch between them. With this versatility, it's the preferred visualization for viewing multiple data types, aiding in your data analysis needs.
{{< figure src="/static/img/docs/tables/table_visualization.png" max-width="1200px" lightbox="true" caption="Table visualization" >}}
{{< figure src="/static/img/docs/tables/table_visualization.png" max-width="1200px" lightbox="true" alt="Table visualization" >}}
You can use a table visualization to show datasets such as:
- Common database queries like logs, traces, metrics
- Financial reports
- Customer lists
- Product catalogs
Any information you might want to put in a spreadsheet can often be best visualized in a table.
Tables also provide different styles to visualize data inside the table cells such as colored text and cell backgrounds, gauges, sparklines, data links, JSON code, and images.
## Configure a table visualization
The following video provides a visual walkthrough of the options you can set in a table visualization. If you want to see a configuration in action, check out the video:
@@ -67,6 +95,38 @@ The following video provides a visual walkthrough of the options you can set in
Annotations and alerts are not currently supported for tables.
{{< /admonition >}}
## Supported data formats
The table visualization supports any data that has a column-row structure.
### Example
```
Column1, Column2, Column3
value1 , value2 , value3
value4 , value5 , value6
value7 , value8 , value9
```
If a cell is missing or the table cell-row structure is not complete, the table visualization wont display any of the data:
```
Column1, Column2, Column3
value1 , value2 , value3
gap1 , gap2
value4 , value5 , value6
```
If you need to hide columns, you can do so using [data transformations](ref:data-transformation), [field overrides](#field-overrides), or by [building a query](ref:build-query) that returns only the needed columns.
If youre using a cell type such as sparkline or JSON, the data requirements may differ in a way thats specific to that type. For more info refer to [Cell type](#cell-type).
## Debugging in tables
The table visualization helps with debugging when you need to know exactly what results your query is returning and why other visualizations might not be working. This functionality is also accessible in most visualizations by toggling on the **Table view** switch at the top of the panel:
![The Table view switch](/media/docs/grafana/panels-visualizations/screenshot-table-view-on-11.2.png)
## Sort column
Click a column title to change the sort order from default to descending to ascending. Each time you click, the sort order changes to the next option in the cycle. You can sort multiple columns by holding the `shift` key and clicking the column name.

View File

@@ -64,11 +64,16 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/panel-editor-overview/#data-section
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/panels-visualizations/panel-editor-overview/#data-section
data-transformation:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/panel-editor-overview/#data-section
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/panels-visualizations/panel-editor-overview/#data-section
---
# Time series
Time series visualizations are the default way to visualize data points over intervals of time, as a graph. They can render series as lines, points, or bars and are versatile enough to display almost any time-series data.
Time series visualizations are the default way to show the variations of a set of data values over time. Each data point is matched to a timestamp and this _time series_ is displayed as a graph. The visualization can render series as lines, points, or bars and it's versatile enough to display almost any type of [time-series data](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/fundamentals/timeseries/).
{{< figure src="/static/img/docs/time-series-panel/time_series_small_example.png" max-width="1200px" alt="Time series" >}}
@@ -76,6 +81,14 @@ Time series visualizations are the default way to visualize data points over int
You can migrate from the legacy Graph visualization to the time series visualization. To migrate, open the panel and click the **Migrate** button in the side pane.
{{< /admonition >}}
A time series visualization displays an x-y graph with time progression on the x-axis and the magnitude of the values on the y-axis. This visualization is ideal for displaying large numbers of timed data points that would be hard to track in a table or list.
You can use the time series visualization if you need track:
- Temperature variations throughout the day
- The daily progress of your retirement account
- The distance you jog each day over the course of a year
## Configure a time series visualization
The following video guides you through the creation steps and common customizations of time series visualizations, and is great for beginners:
@@ -86,7 +99,72 @@ The following video guides you through the creation steps and common customizati
## Supported data formats
Time series visualizations require time-series data&mdash;a sequence of measurements, ordered in time, and formatted as a table&mdash;where every row in the table represents one individual measurement at a specific time. Learn more about [time-series data](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/fundamentals/timeseries/).
Time series visualizations require time-series dataa sequence of measurements, ordered in time, and formatted as a tablewhere every row in the table represents one individual measurement at a specific time. Learn more about [time-series data](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/fundamentals/timeseries/).
The dataset must contain at least one numeric field, and in the case of multiple numeric fields, each one is plotted as a new line, point, or bar labeled with the field name in the tooltip.
### Example 1
In the following example, there are three numeric fields represented by three lines in the chart:
| Time | value1 | value2 | value3 |
| ------------------- | ------ | ------ | ------ |
| 2022-11-01 10:00:00 | 1 | 2 | 3 |
| 2022-11-01 11:00:00 | 4 | 5 | 6 |
| 2022-11-01 12:00:00 | 7 | 8 | 9 |
| 2022-11-01 13:00:00 | 4 | 5 | 6 |
![Time series line chart with multiple numeric fields](/media/docs/grafana/panels-visualizations/screenshot-grafana-11.1-timeseries-example1v2.png 'Time series line chart with multiple numeric fields')
If the time field isn't automatically detected, you might need to convert the data to a time format using a [data transformation](ref:data-transformation).
### Example 2
The time series visualization also supports multiple datasets. If all datasets are in the correct format, the visualization plots the numeric fields of all datasets and labels them using the column name of the field.
#### Query1
| Time | value1 | value2 | value3 |
| ------------------- | ------ | ------ | ------ |
| 2022-11-01 10:00:00 | 1 | 2 | 3 |
| 2022-11-01 11:00:00 | 4 | 5 | 6 |
| 2022-11-01 12:00:00 | 7 | 8 | 9 |
#### Query2
| timestamp | number1 | number2 | number3 |
| ------------------- | ------- | ------- | ------- |
| 2022-11-01 10:30:00 | 11 | 12 | 13 |
| 2022-11-01 11:30:00 | 14 | 15 | 16 |
| 2022-11-01 12:30:00 | 17 | 18 | 19 |
| 2022-11-01 13:30:00 | 14 | 15 | 16 |
![Time series line chart with two datasets](/media/docs/grafana/panels-visualizations/screenshot-grafana-11.1-timeseries-example2v2.png 'Time series line chart with two datasets')
### Example 3
If you want to more easily compare events between different, but overlapping, time frames, you can do this by using a time offset while querying the compared dataset:
#### Query1
| Time | value1 | value2 | value3 |
| ------------------- | ------ | ------ | ------ |
| 2022-11-01 10:00:00 | 1 | 2 | 3 |
| 2022-11-01 11:00:00 | 4 | 5 | 6 |
| 2022-11-01 12:00:00 | 7 | 8 | 9 |
#### Query2
| timestamp(-30min) | number1 | number2 | number3 |
| ------------------- | ------- | ------- | ------- |
| 2022-11-01 10:30:00 | 11 | 12 | 13 |
| 2022-11-01 11:30:00 | 14 | 15 | 16 |
| 2022-11-01 12:30:00 | 17 | 18 | 19 |
| 2022-11-01 13:30:00 | 14 | 15 | 16 |
![Time Series Example with second Data Set offset](/media/docs/grafana/panels-visualizations/screenshot-grafana-11.1-timeseries-example3v2.png 'Time Series Example with second Data Set offset')
When you add the offset, the resulting visualization makes the datasets appear to be occurring at the same time so that you can compare them more easily.
## Alert rules

View File

@@ -232,10 +232,7 @@ For more information on how to configure dashboard providers, refer to [Dashboar
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timepicker": {},
"timezone": "browser",
"title": "Cluster",
"version": 0

View File

@@ -1,5 +1,5 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"npmClient": "yarn",
"version": "11.0.4"
"version": "11.0.5"
}

View File

@@ -3,7 +3,7 @@
"license": "AGPL-3.0-only",
"private": true,
"name": "grafana",
"version": "11.0.4",
"version": "11.0.5",
"repository": "github:grafana/grafana",
"scripts": {
"build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/data",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana Data Library",
"keywords": [
"typescript"
@@ -36,7 +36,7 @@
},
"dependencies": {
"@braintree/sanitize-url": "7.0.1",
"@grafana/schema": "11.0.4",
"@grafana/schema": "11.0.5",
"@types/d3-interpolate": "^3.0.0",
"@types/string-hash": "1.1.3",
"d3-interpolate": "3.0.1",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e-selectors",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana End-to-End Test Selectors Library",
"keywords": [
"cli",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana End-to-End Test Library",
"keywords": [
"cli",
@@ -63,8 +63,8 @@
"@babel/core": "7.23.2",
"@babel/preset-env": "7.23.2",
"@cypress/webpack-preprocessor": "5.17.1",
"@grafana/e2e-selectors": "11.0.4",
"@grafana/schema": "11.0.4",
"@grafana/e2e-selectors": "11.0.5",
"@grafana/schema": "11.0.5",
"@grafana/tsconfig": "^1.3.0-rc1",
"@mochajs/json-file-reporter": "^1.2.0",
"babel-loader": "9.1.3",

View File

@@ -1,7 +1,7 @@
{
"name": "@grafana/eslint-plugin",
"description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.",
"version": "11.0.4",
"version": "11.0.5",
"main": "./index.cjs",
"author": "Grafana Labs",
"license": "Apache-2.0",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/flamegraph",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana flamegraph visualization component",
"keywords": [
"grafana",
@@ -44,8 +44,8 @@
],
"dependencies": {
"@emotion/css": "11.11.2",
"@grafana/data": "11.0.4",
"@grafana/ui": "11.0.4",
"@grafana/data": "11.0.5",
"@grafana/ui": "11.0.5",
"@leeoniya/ufuzzy": "1.0.14",
"d3": "^7.8.5",
"lodash": "4.17.21",

View File

@@ -3,7 +3,7 @@
"license": "AGPL-3.0-only",
"name": "@grafana/o11y-ds-frontend",
"private": true,
"version": "11.0.4",
"version": "11.0.5",
"description": "Library to manage traces in Grafana.",
"sideEffects": false,
"repository": {
@@ -18,12 +18,12 @@
},
"dependencies": {
"@emotion/css": "11.11.2",
"@grafana/data": "11.0.4",
"@grafana/e2e-selectors": "11.0.4",
"@grafana/data": "11.0.5",
"@grafana/e2e-selectors": "11.0.5",
"@grafana/experimental": "1.7.10",
"@grafana/runtime": "11.0.4",
"@grafana/schema": "11.0.4",
"@grafana/ui": "11.0.4",
"@grafana/runtime": "11.0.5",
"@grafana/schema": "11.0.5",
"@grafana/ui": "11.0.5",
"react-use": "17.5.0",
"rxjs": "7.8.1",
"tslib": "2.6.2"

View File

@@ -2,7 +2,7 @@
"name": "@grafana/plugin-configs",
"description": "Shared dependencies and files for core plugins",
"private": true,
"version": "11.0.4",
"version": "11.0.5",
"dependencies": {
"tslib": "2.6.2"
},

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "AGPL-3.0-only",
"name": "@grafana/prometheus",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana Prometheus Library",
"keywords": [
"typescript"
@@ -38,12 +38,12 @@
"dependencies": {
"@emotion/css": "11.11.2",
"@floating-ui/react": "0.26.10",
"@grafana/data": "11.0.4",
"@grafana/data": "11.0.5",
"@grafana/experimental": "1.7.10",
"@grafana/faro-web-sdk": "1.5.0",
"@grafana/runtime": "11.0.4",
"@grafana/schema": "11.0.4",
"@grafana/ui": "11.0.4",
"@grafana/runtime": "11.0.5",
"@grafana/schema": "11.0.5",
"@grafana/ui": "11.0.5",
"@leeoniya/ufuzzy": "1.0.14",
"@lezer/common": "1.2.1",
"@lezer/highlight": "1.2.0",
@@ -76,8 +76,8 @@
},
"devDependencies": {
"@emotion/eslint-plugin": "11.11.0",
"@grafana/e2e": "11.0.4",
"@grafana/e2e-selectors": "11.0.4",
"@grafana/e2e": "11.0.5",
"@grafana/e2e-selectors": "11.0.5",
"@grafana/tsconfig": "^1.3.0-rc1",
"@rollup/plugin-image": "3.0.3",
"@rollup/plugin-node-resolve": "15.2.3",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/runtime",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana Runtime Library",
"keywords": [
"grafana",
@@ -37,11 +37,11 @@
"postpack": "mv package.json.bak package.json"
},
"dependencies": {
"@grafana/data": "11.0.4",
"@grafana/e2e-selectors": "11.0.4",
"@grafana/data": "11.0.5",
"@grafana/e2e-selectors": "11.0.5",
"@grafana/faro-web-sdk": "^1.3.6",
"@grafana/schema": "11.0.4",
"@grafana/ui": "11.0.4",
"@grafana/schema": "11.0.5",
"@grafana/ui": "11.0.5",
"history": "4.10.1",
"lodash": "4.17.21",
"rxjs": "7.8.1",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/schema",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana Schema Library",
"keywords": [
"typescript"

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
limit: number;

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip, common.OptionsWithTextFormatting {
/**

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends common.SingleStatBaseOptions {
displayMode: common.BarGaugeDisplayMode;

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export enum VizDisplayMode {
Candles = 'candles',

View File

@@ -10,7 +10,7 @@
import * as ui from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export enum HorizontalConstraint {
Center = 'center',

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface MetricStat {
/**

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
/**

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
selectedSeries: number;

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export type UpdateConfig = {
render: boolean,

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested);

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends common.SingleStatBaseOptions {
minVizHeight: number;

View File

@@ -10,7 +10,7 @@
import * as ui from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
basemap: ui.MapLayerOptions;

View File

@@ -10,7 +10,7 @@
import * as ui from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
/**
* Controls the color mode of the heatmap

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip {
/**

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
dedupStrategy: common.LogsDedupStrategy;

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export enum QueryEditorMode {
Builder = 'builder',

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
/**

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface ArcOption {
/**

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
/**
* Select the pie chart display style.

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends common.SingleStatBaseOptions {
colorMode: common.BigValueColorMode;

View File

@@ -10,7 +10,7 @@
import * as ui from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
/**

View File

@@ -10,7 +10,7 @@
import * as ui from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
/**

View File

@@ -10,7 +10,7 @@
import * as ui from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options {
/**

View File

@@ -8,7 +8,7 @@
//
// Run 'make gen-cue' from repository root to regenerate.
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export enum TextMode {
Code = 'code',

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
export interface Options extends common.OptionsWithTimezones {
legend: common.VizLegendOptions;

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
/**
* Identical to timeseries... except it does not have timezone settings

View File

@@ -10,7 +10,7 @@
import * as common from '@grafana/schema';
export const pluginVersion = "11.0.4";
export const pluginVersion = "11.0.5";
/**
* Auto is "table" in the UI

View File

@@ -3,7 +3,7 @@
"license": "AGPL-3.0-only",
"private": true,
"name": "@grafana/sql",
"version": "11.0.4",
"version": "11.0.5",
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git",
@@ -15,10 +15,10 @@
},
"dependencies": {
"@emotion/css": "11.11.2",
"@grafana/data": "11.0.4",
"@grafana/data": "11.0.5",
"@grafana/experimental": "1.7.10",
"@grafana/runtime": "11.0.4",
"@grafana/ui": "11.0.4",
"@grafana/runtime": "11.0.5",
"@grafana/ui": "11.0.5",
"@react-awesome-query-builder/ui": "6.4.2",
"@types/lodash": "4.17.0",
"@types/react-virtualized-auto-sizer": "1.0.4",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/ui",
"version": "11.0.4",
"version": "11.0.5",
"description": "Grafana Components Library",
"keywords": [
"grafana",
@@ -50,10 +50,10 @@
"@emotion/css": "11.11.2",
"@emotion/react": "11.11.4",
"@floating-ui/react": "0.26.10",
"@grafana/data": "11.0.4",
"@grafana/e2e-selectors": "11.0.4",
"@grafana/data": "11.0.5",
"@grafana/e2e-selectors": "11.0.5",
"@grafana/faro-web-sdk": "^1.3.6",
"@grafana/schema": "11.0.4",
"@grafana/schema": "11.0.5",
"@leeoniya/ufuzzy": "1.0.14",
"@monaco-editor/react": "4.6.0",
"@popperjs/core": "2.11.8",

View File

@@ -72,6 +72,7 @@ func WithUpdatedVersion(d *dagger.Client, src *dagger.Directory, nodeVersion, ve
WithExec([]string{"npm", "version", version, "--no-git-tag-version"}).
WithExec([]string{"yarn", "run", "lerna", "version", version, "--no-push", "--no-git-tag-version", "--force-publish", "--exact", "--yes"}).
WithExec([]string{"yarn", "install"}).
WithExec([]string{"yarn", "prettier:write"}).
Directory("/src").
WithoutDirectory("node_modules")
}

View File

@@ -11,6 +11,8 @@ import (
"strconv"
"strings"
"time"
"github.com/urfave/cli/v2"
)
const (
@@ -30,6 +32,12 @@ func logError(message string, err error) int {
return 1
}
func RunCmdCLI(c *cli.Context) error {
os.Exit(RunCmd())
return nil
}
// RunCmd runs the build command and returns the exit code
func RunCmd() int {
opts := BuildOptsFromFlags()

View File

@@ -2,20 +2,6 @@ package main
import "github.com/urfave/cli/v2"
// ArgCountWrapper will cause the action to fail if there were not exactly `num` args provided.
func ArgCountWrapper(num int, action cli.ActionFunc) cli.ActionFunc {
return func(ctx *cli.Context) error {
if ctx.NArg() != num {
if err := cli.ShowSubcommandHelp(ctx); err != nil {
return cli.Exit(err.Error(), 1)
}
return cli.Exit("", 1)
}
return action(ctx)
}
}
// ArgCountWrapper will cause the action to fail if there were more than `num` args provided.
func MaxArgCountWrapper(max int, action cli.ActionFunc) cli.ActionFunc {
return func(ctx *cli.Context) error {

View File

@@ -1,68 +0,0 @@
package main
import (
"fmt"
"log"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/compilers"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/errutil"
"github.com/grafana/grafana/pkg/build/grafana"
"github.com/grafana/grafana/pkg/build/syncutil"
)
func BuildBackend(ctx *cli.Context) error {
metadata, err := config.GenerateMetadata(ctx)
if err != nil {
return err
}
version := metadata.GrafanaVersion
var (
edition = config.Edition(ctx.String("edition"))
cfg = config.Config{
NumWorkers: ctx.Int("jobs"),
}
)
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
if err != nil {
return fmt.Errorf("could not get version / package info for mode '%s': %w", metadata.ReleaseMode.Mode, err)
}
const grafanaDir = "."
log.Printf("Building Grafana back-end, version %q, %s edition, variants [%v]",
version, edition, buildConfig.Variants)
p := syncutil.NewWorkerPool(cfg.NumWorkers)
defer p.Close()
if err := compilers.Install(); err != nil {
return cli.Exit(err.Error(), 1)
}
g, _ := errutil.GroupWithContext(ctx.Context)
for _, variant := range buildConfig.Variants {
variant := variant
opts := grafana.BuildVariantOpts{
Variant: variant,
Edition: edition,
Version: version,
GrafanaDir: grafanaDir,
}
p.Schedule(g.Wrap(func() error {
return grafana.BuildVariant(ctx.Context, opts)
}))
}
if err := g.Wait(); err != nil {
return cli.Exit(err.Error(), 1)
}
log.Println("Successfully built back-end binaries!")
return nil
}

View File

@@ -1,51 +0,0 @@
package main
import (
"log"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/docker"
"github.com/grafana/grafana/pkg/build/gcloud"
)
func BuildDocker(c *cli.Context) error {
if err := docker.Init(); err != nil {
return err
}
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
useUbuntu := c.Bool("ubuntu")
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
if err != nil {
return err
}
shouldSave := buildConfig.Docker.ShouldSave
if shouldSave {
if err := gcloud.ActivateServiceAccount(); err != nil {
return err
}
}
edition := config.Edition(c.String("edition"))
version := metadata.GrafanaVersion
log.Printf("Building Docker images, version %s, %s edition, Ubuntu based: %v...", version, edition,
useUbuntu)
for _, arch := range buildConfig.Docker.Architectures {
if _, err := docker.BuildImage(version, arch, ".", useUbuntu, shouldSave, edition, metadata.ReleaseMode.Mode); err != nil {
return cli.Exit(err.Error(), 1)
}
}
log.Println("Successfully built Docker images!")
return nil
}

View File

@@ -1,39 +0,0 @@
package main
import (
"log"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/errutil"
"github.com/grafana/grafana/pkg/build/frontend"
"github.com/grafana/grafana/pkg/build/syncutil"
)
func BuildFrontend(c *cli.Context) error {
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
cfg, mode, err := frontend.GetConfig(c, metadata)
if err != nil {
return err
}
p := syncutil.NewWorkerPool(cfg.NumWorkers)
defer p.Close()
g, _ := errutil.GroupWithContext(c.Context)
if err := frontend.Build(mode, frontend.GrafanaDir, p, g); err != nil {
return err
}
if err := g.Wait(); err != nil {
return err
}
log.Println("Successfully built Grafana front-end!")
return nil
}

View File

@@ -1,38 +0,0 @@
package main
import (
"log"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/errutil"
"github.com/grafana/grafana/pkg/build/frontend"
"github.com/grafana/grafana/pkg/build/syncutil"
"github.com/urfave/cli/v2"
)
func BuildFrontendPackages(c *cli.Context) error {
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
cfg, mode, err := frontend.GetConfig(c, metadata)
if err != nil {
return err
}
p := syncutil.NewWorkerPool(cfg.NumWorkers)
defer p.Close()
g, _ := errutil.GroupWithContext(c.Context)
if err := frontend.BuildFrontendPackages(cfg.PackageVersion, mode, frontend.GrafanaDir, p, g); err != nil {
return cli.Exit(err.Error(), 1)
}
if err := g.Wait(); err != nil {
return cli.Exit(err.Error(), 1)
}
log.Println("Successfully built Grafana front-end packages!")
return nil
}

View File

@@ -1,53 +0,0 @@
package main
import (
"context"
"log"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/errutil"
"github.com/grafana/grafana/pkg/build/plugins"
"github.com/grafana/grafana/pkg/build/syncutil"
)
func BuildInternalPlugins(c *cli.Context) error {
cfg := config.Config{
NumWorkers: c.Int("jobs"),
}
const grafanaDir = "."
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
if err != nil {
return err
}
log.Println("Building internal Grafana plug-ins...")
ctx := context.Background()
p := syncutil.NewWorkerPool(cfg.NumWorkers)
defer p.Close()
var g *errutil.Group
g, ctx = errutil.GroupWithContext(ctx)
if err := plugins.Build(ctx, grafanaDir, p, g, buildConfig); err != nil {
return cli.Exit(err.Error(), 1)
}
if err := g.Wait(); err != nil {
return cli.Exit(err.Error(), 1)
}
if err := plugins.Download(ctx, grafanaDir, p); err != nil {
return cli.Exit(err.Error(), 1)
}
log.Println("Successfully built Grafana plug-ins!")
return nil
}

View File

@@ -1,133 +0,0 @@
package main
import (
"fmt"
"log"
"os"
"strconv"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/env"
"github.com/grafana/grafana/pkg/build/git"
)
// checkOpts are options used to create a new GitHub check for the enterprise downstream test.
type checkOpts struct {
SHA string
URL string
Branch string
PR int
}
func getCheckOpts(args []string) (*checkOpts, error) {
branch, ok := env.Lookup("DRONE_SOURCE_BRANCH", args)
if !ok {
return nil, cli.Exit("Unable to retrieve build source branch", 1)
}
var (
rgx = git.PRCheckRegexp()
matches = rgx.FindStringSubmatch(branch)
)
sha, ok := env.Lookup("SOURCE_COMMIT", args)
if !ok {
if matches == nil || len(matches) <= 1 {
return nil, cli.Exit("Unable to retrieve source commit", 1)
}
sha = matches[2]
}
url, ok := env.Lookup("DRONE_BUILD_LINK", args)
if !ok {
return nil, cli.Exit(`missing environment variable "DRONE_BUILD_LINK"`, 1)
}
prStr, ok := env.Lookup("OSS_PULL_REQUEST", args)
if !ok {
if matches == nil || len(matches) <= 1 {
return nil, cli.Exit("Unable to retrieve PR number", 1)
}
prStr = matches[1]
}
pr, err := strconv.Atoi(prStr)
if err != nil {
return nil, err
}
return &checkOpts{
Branch: branch,
PR: pr,
SHA: sha,
URL: url,
}, nil
}
// EnterpriseCheckBegin creates the GitHub check and signals the beginning of the downstream build / test process
func EnterpriseCheckBegin(c *cli.Context) error {
var (
ctx = c.Context
client = git.NewGitHubClient(ctx, c.String("github-token"))
)
opts, err := getCheckOpts(os.Environ())
if err != nil {
return err
}
if _, err = git.CreateEnterpriseStatus(ctx, client.Repositories, opts.SHA, opts.URL, "pending"); err != nil {
return err
}
return nil
}
func EnterpriseCheckSuccess(c *cli.Context) error {
return completeEnterpriseCheck(c, true)
}
func EnterpriseCheckFail(c *cli.Context) error {
return completeEnterpriseCheck(c, false)
}
func completeEnterpriseCheck(c *cli.Context, success bool) error {
var (
ctx = c.Context
client = git.NewGitHubClient(ctx, c.String("github-token"))
)
// Update the pull request labels
opts, err := getCheckOpts(os.Environ())
if err != nil {
return err
}
status := "failure"
if success {
status = "success"
}
// Update the GitHub check...
if _, err := git.CreateEnterpriseStatus(ctx, client.Repositories, opts.SHA, opts.URL, status); err != nil {
return err
}
// Delete branch if needed
log.Printf("Checking branch '%s' against '%s'", git.PRCheckRegexp().String(), opts.Branch)
if git.PRCheckRegexp().MatchString(opts.Branch) {
log.Println("Deleting branch", opts.Branch)
if err := git.DeleteEnterpriseBranch(ctx, client.Git, opts.Branch); err != nil {
return fmt.Errorf("error deleting enterprise branch: %w", err)
}
}
label := "enterprise-failed"
if success {
label = "enterprise-ok"
}
return git.AddLabelToPR(ctx, client.Issues, opts.PR, label)
}

View File

@@ -1,69 +0,0 @@
package main
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestGetCheckOpts(t *testing.T) {
t.Run("it should return the checkOpts if the correct environment variables are set", func(t *testing.T) {
args := []string{
"SOURCE_COMMIT=1234",
"DRONE_SOURCE_BRANCH=test",
"DRONE_BUILD_LINK=http://example.com",
"OSS_PULL_REQUEST=1",
}
opts, err := getCheckOpts(args)
require.NoError(t, err)
require.Equal(t, opts.SHA, "1234")
require.Equal(t, opts.URL, "http://example.com")
})
t.Run("it should return an error if SOURCE_COMMIT is not set", func(t *testing.T) {
args := []string{
"DRONE_BUILD_LINK=http://example.com",
"DRONE_SOURCE_BRANCH=test",
"DRONE_BUILD_LINK=http://example.com",
"OSS_PULL_REQUEST=1",
}
opts, err := getCheckOpts(args)
require.Nil(t, opts)
require.Error(t, err)
})
t.Run("it should return an error if DRONE_BUILD_LINK is not set", func(t *testing.T) {
args := []string{
"SOURCE_COMMIT=1234",
"DRONE_SOURCE_BRANCH=test",
"OSS_PULL_REQUEST=1",
}
opts, err := getCheckOpts(args)
require.Nil(t, opts)
require.Error(t, err)
})
t.Run("it should return an error if OSS_PULL_REQUEST is not set", func(t *testing.T) {
args := []string{
"SOURCE_COMMIT=1234",
"DRONE_SOURCE_BRANCH=test",
"DRONE_BUILD_LINK=http://example.com",
}
opts, err := getCheckOpts(args)
require.Nil(t, opts)
require.Error(t, err)
})
t.Run("it should return an error if OSS_PULL_REQUEST is not an integer", func(t *testing.T) {
args := []string{
"SOURCE_COMMIT=1234",
"DRONE_SOURCE_BRANCH=test",
"DRONE_BUILD_LINK=http://example.com",
"OSS_PULL_REQUEST=http://example.com",
}
opts, err := getCheckOpts(args)
require.Nil(t, opts)
require.Error(t, err)
})
}

View File

@@ -1,32 +0,0 @@
package main
import (
"os"
"path/filepath"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/config"
)
func ExportVersion(c *cli.Context) error {
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
const distDir = "dist"
if err := os.RemoveAll(distDir); err != nil {
return err
}
if err := os.Mkdir(distDir, 0750); err != nil {
return err
}
// nolint:gosec
if err := os.WriteFile(filepath.Join(distDir, "grafana.version"), []byte(metadata.GrafanaVersion), 0664); err != nil {
return err
}
return nil
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"log"
"os/exec"
"strings"
"path/filepath"
"github.com/urfave/cli/v2"
@@ -18,6 +18,25 @@ const (
ubuntu = "ubuntu"
)
// GetImageFiles returns the list of image (.img, but should be .tar because they are tar archives) files that are
// created in the 'tag' process and stored in the prerelease bucket, waiting to be released.
func GetImageFiles(grafana string, version string, architectures []config.Architecture) []string {
bases := []string{alpine, ubuntu}
images := []string{}
for _, base := range bases {
for _, arch := range architectures {
image := fmt.Sprintf("%s-%s-%s.img", grafana, version, arch)
if base == "ubuntu" {
image = fmt.Sprintf("%s-%s-ubuntu-%s.img", grafana, version, arch)
}
images = append(images, image)
}
}
return images
}
func FetchImages(c *cli.Context) error {
if c.NArg() > 0 {
if err := cli.ShowSubcommandHelp(c); err != nil {
@@ -44,74 +63,65 @@ func FetchImages(c *cli.Context) error {
Tag: metadata.GrafanaVersion,
}
edition := fmt.Sprintf("-%s", cfg.Edition)
grafana := "grafana"
if cfg.Edition == "enterprise" {
grafana = "grafana-enterprise"
}
if cfg.Edition == "enterprise2" {
grafana = "grafana-enterprise2"
}
if cfg.Edition == "grafana" || cfg.Edition == "oss" {
grafana = "grafana-oss"
}
err = gcloud.ActivateServiceAccount()
if err != nil {
baseURL := fmt.Sprintf("gs://%s/%s/", cfg.Bucket, cfg.Tag)
images := GetImageFiles(grafana, cfg.Tag, cfg.Archs)
log.Printf("Fetching images [%v]", images)
if err := gcloud.ActivateServiceAccount(); err != nil {
return err
}
var basesStr []string
for _, base := range cfg.Distribution {
switch base {
case alpine:
basesStr = append(basesStr, "")
case ubuntu:
basesStr = append(basesStr, "-ubuntu")
default:
return fmt.Errorf("unrecognized base %q", base)
}
}
err = downloadFromGCS(cfg, basesStr, edition)
if err != nil {
if err := DownloadImages(baseURL, images, "."); err != nil {
return err
}
err = loadImages(cfg, basesStr, edition)
if err != nil {
if err := LoadImages(images, "."); err != nil {
return err
}
return nil
}
func loadImages(cfg docker.Config, basesStr []string, edition string) error {
log.Println("Loading fetched image files to local docker registry...")
log.Printf("Number of images to be loaded: %d\n", len(basesStr)*len(cfg.Archs))
for _, base := range basesStr {
for _, arch := range cfg.Archs {
imageFilename := fmt.Sprintf("grafana%s-%s%s-%s.img", edition, cfg.Tag, base, arch)
log.Printf("image file name: %s\n", imageFilename)
//nolint:gosec
cmd := exec.Command("docker", "load", "-i", imageFilename)
cmd.Dir = "."
out, err := cmd.CombinedOutput()
if err != nil {
log.Printf("out: %s\n", out)
return fmt.Errorf("error loading image: %q", err)
}
log.Printf("Successfully loaded %s!\n %s\n", fmt.Sprintf("grafana%s-%s%s-%s", edition, cfg.Tag, base, arch), out)
// LoadImages uses the `docker load -i` command to load the image tar file into the docker daemon so that it can be
// tagged and pushed.
func LoadImages(images []string, source string) error {
p := filepath.Clean(source)
for _, image := range images {
image := filepath.Join(p, image)
log.Println("Loading image", image)
//nolint:gosec
cmd := exec.Command("docker", "load", "-i", image)
cmd.Dir = "."
out, err := cmd.CombinedOutput()
if err != nil {
log.Printf("out: %s\n", out)
return fmt.Errorf("error loading image: %q", err)
}
log.Println("Loaded image", image)
}
log.Println("Images successfully loaded!")
return nil
}
func downloadFromGCS(cfg docker.Config, basesStr []string, edition string) error {
log.Printf("Downloading Docker images from GCS bucket: %s\n", cfg.Bucket)
for _, base := range basesStr {
for _, arch := range cfg.Archs {
src := fmt.Sprintf("gs://%s/%s/grafana%s-%s%s-%s.img", cfg.Bucket, cfg.Tag, edition, cfg.Tag, base, arch)
args := strings.Split(fmt.Sprintf("-m cp -r %s .", src), " ")
//nolint:gosec
cmd := exec.Command("gsutil", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to download: %w\n%s", err, out)
}
func DownloadImages(baseURL string, images []string, destination string) error {
for _, image := range images {
p := baseURL + image
log.Println("Downloading image", p)
//nolint:gosec
cmd := exec.Command("gsutil", "-m", "cp", "-r", p, destination)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to download: %w\n%s", err, out)
}
}
log.Printf("Successfully fetched image files from %s bucket!\n", cfg.Bucket)
return nil
}

View File

@@ -0,0 +1,48 @@
package main
import (
"testing"
"github.com/grafana/grafana/pkg/build/config"
"github.com/stretchr/testify/require"
)
func TestGetImageFiles(t *testing.T) {
var (
architectures = []config.Architecture{
config.ArchAMD64,
config.ArchARM64,
config.ArchARMv7,
}
)
t.Run("1.2.3", func(t *testing.T) {
expect := []string{
"grafana-oss-1.2.3-amd64.img",
"grafana-oss-1.2.3-arm64.img",
"grafana-oss-1.2.3-armv7.img",
"grafana-oss-1.2.3-ubuntu-amd64.img",
"grafana-oss-1.2.3-ubuntu-arm64.img",
"grafana-oss-1.2.3-ubuntu-armv7.img",
}
res := GetImageFiles("grafana-oss", "1.2.3", architectures)
require.Equal(t, expect, res)
})
t.Run("1.2.3+example-01", func(t *testing.T) {
expect := []string{
"grafana-oss-1.2.3+example-01-amd64.img",
"grafana-oss-1.2.3+example-01-arm64.img",
"grafana-oss-1.2.3+example-01-armv7.img",
"grafana-oss-1.2.3+example-01-ubuntu-amd64.img",
"grafana-oss-1.2.3+example-01-ubuntu-arm64.img",
"grafana-oss-1.2.3+example-01-ubuntu-armv7.img",
}
res := GetImageFiles("grafana-oss", "1.2.3+example-01", architectures)
require.Equal(t, expect, res)
})
}

View File

@@ -16,37 +16,15 @@ var (
Usage: "The edition of Grafana to build (oss or enterprise)",
Value: "oss",
}
variantsFlag = cli.StringFlag{
Name: "variants",
Usage: "Comma-separated list of variants to build",
}
triesFlag = cli.IntFlag{
Name: "tries",
Usage: "Specify number of tries before failing",
Value: 1,
}
noInstallDepsFlag = cli.BoolFlag{
Name: "no-install-deps",
Usage: "Don't install dependencies",
}
signingAdminFlag = cli.BoolFlag{
Name: "signing-admin",
Usage: "Use manifest signing admin API endpoint?",
}
signFlag = cli.BoolFlag{
Name: "sign",
Usage: "Enable plug-in signing (you must set GRAFANA_API_KEY)",
}
dryRunFlag = cli.BoolFlag{
Name: "dry-run",
Usage: "Only simulate actions",
}
gitHubTokenFlag = cli.StringFlag{
Name: "github-token",
Value: "",
EnvVars: []string{"GITHUB_TOKEN"},
Usage: "GitHub token",
}
tagFlag = cli.StringFlag{
Name: "tag",
Usage: "Grafana version tag",

View File

@@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/gcloud"
"github.com/grafana/grafana/pkg/build/gcloud/storage"
"github.com/grafana/grafana/pkg/build/gcom"
"github.com/grafana/grafana/pkg/build/packaging"
)
@@ -125,6 +126,48 @@ func getReleaseURLs() (string, string, error) {
return pconf.Grafana.WhatsNewURL, pconf.Grafana.ReleaseNotesURL, nil
}
func Builds(baseURL *url.URL, grafana, version string, packages []packaging.BuildArtifact) ([]GCOMPackage, error) {
builds := make([]GCOMPackage, len(packages))
for i, v := range packages {
var (
os = v.Distro
arch = v.Arch
)
if v.Distro == "windows" {
os = "win"
if v.Ext == "msi" {
os = "win-installer"
}
}
if v.Distro == "rhel" {
if arch == "aarch64" {
arch = "arm64"
}
}
if v.Distro == "deb" {
if arch == "armhf" {
arch = "armv7"
if v.RaspberryPi {
log.Println(v.Distro, arch, "raspberrypi == true")
arch = "armv6"
}
}
}
u := gcom.GetURL(baseURL, version, grafana, v.Distro, v.Arch, v.Ext, v.Musl, v.RaspberryPi)
builds[i] = GCOMPackage{
OS: os,
URL: u.String(),
Arch: arch,
}
}
return builds, nil
}
// publishPackages publishes packages to grafana.com.
func publishPackages(cfg packaging.PublishConfig) error {
log.Printf("Publishing Grafana packages, version %s, %s edition, %s mode, dryRun: %v, simulating: %v...\n",
@@ -133,14 +176,17 @@ func publishPackages(cfg packaging.PublishConfig) error {
versionStr := fmt.Sprintf("v%s", cfg.Version)
log.Printf("Creating release %s at grafana.com...\n", versionStr)
var sfx string
var pth string
var (
pth string
grafana = "grafana"
)
switch cfg.Edition {
case config.EditionOSS:
pth = "oss"
case config.EditionEnterprise:
grafana = "grafana-enterprise"
pth = "enterprise"
sfx = packaging.EnterpriseSfx
default:
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
}
@@ -152,28 +198,19 @@ func publishPackages(cfg packaging.PublishConfig) error {
pth = path.Join(pth, packaging.ReleaseFolder)
}
product := fmt.Sprintf("grafana%s", sfx)
pth = path.Join(pth, product)
baseArchiveURL := fmt.Sprintf("https://dl.grafana.com/%s", pth)
var builds []buildRepr
for _, ba := range packaging.ArtifactConfigs {
u := ba.GetURL(baseArchiveURL, cfg)
sha256, err := getSHA256(u)
if err != nil {
return err
}
builds = append(builds, buildRepr{
OS: ba.Os,
URL: u,
SHA256: string(sha256),
Arch: ba.Arch,
})
pth = path.Join(pth)
baseArchiveURL := &url.URL{
Scheme: "https",
Host: "dl.grafana.com",
Path: pth,
}
r := releaseRepr{
builds, err := Builds(baseArchiveURL, grafana, cfg.Version, packaging.ArtifactConfigs)
if err != nil {
return err
}
r := Release{
Version: cfg.Version,
ReleaseDate: time.Now().UTC(),
Builds: builds,
@@ -195,6 +232,15 @@ func publishPackages(cfg packaging.PublishConfig) error {
return err
}
for i, v := range r.Builds {
sha, err := getSHA256(v.URL)
if err != nil {
return err
}
r.Builds[i].SHA256 = string(sha)
}
for _, b := range r.Builds {
if err := postRequest(cfg, fmt.Sprintf("versions/%s/packages", cfg.Version), b,
fmt.Sprintf("create build %s %s", b.OS, b.Arch)); err != nil {
@@ -211,6 +257,7 @@ func publishPackages(cfg packaging.PublishConfig) error {
func getSHA256(u string) ([]byte, error) {
shaURL := fmt.Sprintf("%s.sha256", u)
// nolint:gosec
resp, err := http.Get(shaURL)
if err != nil {
@@ -232,7 +279,7 @@ func getSHA256(u string) ([]byte, error) {
return sha256, nil
}
func postRequest(cfg packaging.PublishConfig, pth string, obj any, descr string) error {
func postRequest(cfg packaging.PublishConfig, pth string, body any, descr string) error {
var sfx string
switch cfg.Edition {
case config.EditionOSS:
@@ -243,7 +290,7 @@ func postRequest(cfg packaging.PublishConfig, pth string, obj any, descr string)
}
product := fmt.Sprintf("grafana%s", sfx)
jsonB, err := json.Marshal(obj)
jsonB, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to JSON encode release: %w", err)
}
@@ -303,20 +350,20 @@ func constructURL(product string, pth string) (string, error) {
return u.String(), err
}
type buildRepr struct {
type GCOMPackage struct {
OS string `json:"os"`
URL string `json:"url"`
SHA256 string `json:"sha256"`
Arch string `json:"arch"`
}
type releaseRepr struct {
Version string `json:"version"`
ReleaseDate time.Time `json:"releaseDate"`
Stable bool `json:"stable"`
Beta bool `json:"beta"`
Nightly bool `json:"nightly"`
WhatsNewURL string `json:"whatsNewUrl"`
ReleaseNotesURL string `json:"releaseNotesUrl"`
Builds []buildRepr `json:"-"`
type Release struct {
Version string `json:"version"`
ReleaseDate time.Time `json:"releaseDate"`
Stable bool `json:"stable"`
Beta bool `json:"beta"`
Nightly bool `json:"nightly"`
WhatsNewURL string `json:"whatsNewUrl"`
ReleaseNotesURL string `json:"releaseNotesUrl"`
Builds []GCOMPackage `json:"-"`
}

View File

@@ -1,7 +1,14 @@
package main
import (
"fmt"
"net/url"
"path"
"testing"
"github.com/grafana/grafana/pkg/build/packaging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_constructURL(t *testing.T) {
@@ -33,3 +40,221 @@ func Test_constructURL(t *testing.T) {
})
}
}
func TestBuilds(t *testing.T) {
baseURL := &url.URL{
Scheme: "https",
Host: "dl.example.com",
Path: path.Join("oss", "release"),
}
version := "1.2.3"
grafana := "grafana"
packages := []packaging.BuildArtifact{
{
Distro: "deb",
Arch: "arm64",
Ext: "deb",
},
{
Distro: "rhel",
Arch: "aarch64",
Ext: "rpm",
},
{
Distro: "linux",
Arch: "arm64",
Ext: "tar.gz",
},
{
Distro: "deb",
Arch: "armhf",
Ext: "deb",
RaspberryPi: true,
},
{
Distro: "deb",
Arch: "armhf",
Ext: "deb",
},
{
Distro: "linux",
Arch: "armv7",
Ext: "tar.gz",
},
{
Distro: "windows",
Arch: "amd64",
Ext: "zip",
},
{
Distro: "windows",
Arch: "amd64",
Ext: "msi",
},
}
expect := []GCOMPackage{
{
URL: "https://dl.example.com/oss/release/grafana_1.2.3_arm64.deb",
OS: "deb",
Arch: "arm64",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3-1.aarch64.rpm",
OS: "rhel",
Arch: "arm64",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3.linux-arm64.tar.gz",
OS: "linux",
Arch: "arm64",
},
{
URL: "https://dl.example.com/oss/release/grafana-rpi_1.2.3_armhf.deb",
OS: "deb",
Arch: "armv6",
},
{
URL: "https://dl.example.com/oss/release/grafana_1.2.3_armhf.deb",
OS: "deb",
Arch: "armv7",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3.linux-armv7.tar.gz",
OS: "linux",
Arch: "armv7",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3.windows-amd64.zip",
OS: "win",
Arch: "amd64",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3.windows-amd64.msi",
OS: "win-installer",
Arch: "amd64",
},
}
builds, err := Builds(baseURL, grafana, version, packages)
require.NoError(t, err)
require.Equal(t, len(expect), len(builds))
for i := range builds {
t.Run(fmt.Sprintf("[%d/%d] %s", i+1, len(builds), expect[i].URL), func(t *testing.T) {
assert.Equal(t, expect[i].URL, builds[i].URL)
assert.Equal(t, expect[i].OS, builds[i].OS)
assert.Equal(t, expect[i].Arch, builds[i].Arch)
})
}
}
func TestBuildsWithPlus(t *testing.T) {
baseURL := &url.URL{
Scheme: "https",
Host: "dl.example.com",
Path: path.Join("oss", "release"),
}
version := "1.2.3+example-01"
grafana := "grafana"
packages := []packaging.BuildArtifact{
{
Distro: "deb",
Arch: "arm64",
Ext: "deb",
},
{
Distro: "rhel",
Arch: "aarch64",
Ext: "rpm",
},
{
Distro: "linux",
Arch: "arm64",
Ext: "tar.gz",
},
{
Distro: "deb",
Arch: "armhf",
Ext: "deb",
RaspberryPi: true,
},
{
Distro: "deb",
Arch: "armhf",
Ext: "deb",
},
{
Distro: "linux",
Arch: "armv7",
Ext: "tar.gz",
},
{
Distro: "windows",
Arch: "amd64",
Ext: "zip",
},
{
Distro: "windows",
Arch: "amd64",
Ext: "msi",
},
}
expect := []GCOMPackage{
{
URL: "https://dl.example.com/oss/release/grafana_1.2.3+example~01_arm64.deb",
OS: "deb",
Arch: "arm64",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example~01-1.aarch64.rpm",
OS: "rhel",
Arch: "arm64",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.linux-arm64.tar.gz",
OS: "linux",
Arch: "arm64",
},
{
URL: "https://dl.example.com/oss/release/grafana-rpi_1.2.3+example~01_armhf.deb",
OS: "deb",
Arch: "armv6",
},
{
URL: "https://dl.example.com/oss/release/grafana_1.2.3+example~01_armhf.deb",
OS: "deb",
Arch: "armv7",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.linux-armv7.tar.gz",
OS: "linux",
Arch: "armv7",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.windows-amd64.zip",
OS: "win",
Arch: "amd64",
},
{
URL: "https://dl.example.com/oss/release/grafana-1.2.3+example-01.windows-amd64.msi",
OS: "win-installer",
Arch: "amd64",
},
}
builds, err := Builds(baseURL, grafana, version, packages)
require.NoError(t, err)
require.Equal(t, len(expect), len(builds))
for i := range builds {
t.Run(fmt.Sprintf("[%d/%d] %s", i+1, len(builds), expect[i].URL), func(t *testing.T) {
assert.Equal(t, expect[i].URL, builds[i].URL)
assert.Equal(t, expect[i].OS, builds[i].OS)
assert.Equal(t, expect[i].Arch, builds[i].Arch)
})
}
}

View File

@@ -3,11 +3,9 @@ package main
import (
"log"
"os"
"strings"
"github.com/grafana/grafana/pkg/build"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/docker"
)
var additionalCommands []*cli.Command = make([]*cli.Command, 0, 5)
@@ -21,28 +19,8 @@ func main() {
app := cli.NewApp()
app.Commands = cli.Commands{
{
Name: "build-backend",
Usage: "Build one or more variants of back-end binaries",
ArgsUsage: "[version]",
Action: MaxArgCountWrapper(1, BuildBackend),
Flags: []cli.Flag{
&jobsFlag,
&variantsFlag,
&editionFlag,
&buildIDFlag,
},
},
{
Name: "build-frontend-packages",
Usage: "Build front-end packages",
ArgsUsage: "[version]",
Action: BuildFrontendPackages,
Flags: []cli.Flag{
&jobsFlag,
&editionFlag,
&buildIDFlag,
&noInstallDepsFlag,
},
Name: "build",
Action: build.RunCmdCLI,
},
{
Name: "e2e-tests",
@@ -71,44 +49,11 @@ func main() {
},
},
},
{
Name: "build-frontend",
Usage: "Build front-end artifacts",
ArgsUsage: "[version]",
Action: MaxArgCountWrapper(1, BuildFrontend),
Flags: []cli.Flag{
&jobsFlag,
&editionFlag,
&buildIDFlag,
},
},
{
Name: "whatsnew-checker",
Usage: "Checks whatsNewUrl in package.json for differences between the tag and the docs version",
Action: WhatsNewChecker,
},
{
Name: "build-docker",
Usage: "Build Grafana Docker images",
Action: MaxArgCountWrapper(1, BuildDocker),
Flags: []cli.Flag{
&jobsFlag,
&editionFlag,
&cli.BoolFlag{
Name: "ubuntu",
Usage: "Use Ubuntu base image",
},
&cli.BoolFlag{
Name: "shouldSave",
Usage: "Should save docker image to tarball",
},
&cli.StringFlag{
Name: "archs",
Value: strings.Join(docker.AllArchs, ","),
Usage: "Comma separated architectures to build",
},
},
},
{
Name: "upload-cdn",
Usage: "Upload public/* to a cdn bucket",
@@ -117,23 +62,6 @@ func main() {
&editionFlag,
},
},
{
Name: "shellcheck",
Usage: "Run shellcheck on shell scripts",
Action: Shellcheck,
},
{
Name: "build-plugins",
Usage: "Build internal plug-ins",
Action: MaxArgCountWrapper(1, BuildInternalPlugins),
Flags: []cli.Flag{
&jobsFlag,
&editionFlag,
&signingAdminFlag,
&signFlag,
&noInstallDepsFlag,
},
},
{
Name: "publish-metrics",
Usage: "Publish a set of metrics from stdin",
@@ -145,30 +73,6 @@ func main() {
Usage: "Verify Drone configuration",
Action: VerifyDrone,
},
{
Name: "verify-starlark",
Usage: "Verify Starlark configuration",
ArgsUsage: "<workspace path>",
Action: VerifyStarlark,
},
{
Name: "export-version",
Usage: "Exports version in dist/grafana.version",
Action: ExportVersion,
},
{
Name: "package",
Usage: "Package one or more Grafana variants",
ArgsUsage: "[version]",
Action: MaxArgCountWrapper(1, Package),
Flags: []cli.Flag{
&jobsFlag,
&variantsFlag,
&editionFlag,
&buildIDFlag,
&signFlag,
},
},
{
Name: "store-storybook",
Usage: "Stores storybook to GCS buckets",
@@ -279,18 +183,6 @@ func main() {
&editionFlag,
},
},
{
Name: "publish-enterprise2",
Usage: "Handle Grafana Enterprise2 Docker images",
ArgsUsage: "[version]",
Action: Enterprise2,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dockerhub-repo",
Usage: "DockerHub repo to push images",
},
},
},
},
},
{
@@ -399,36 +291,6 @@ func main() {
},
},
},
{
Name: "enterprise-check",
Usage: "Commands for testing against Grafana Enterprise",
Subcommands: cli.Commands{
{
Name: "begin",
Usage: "Creates the GitHub check in a pull request and begins the tests",
Action: EnterpriseCheckBegin,
Flags: []cli.Flag{
&gitHubTokenFlag,
},
},
{
Name: "success",
Usage: "Updates the GitHub check in a pull request to show a successful build and updates the pull request labels",
Action: EnterpriseCheckSuccess,
Flags: []cli.Flag{
&gitHubTokenFlag,
},
},
{
Name: "fail",
Usage: "Updates the GitHub check in a pull request to show a failed build and updates the pull request labels",
Action: EnterpriseCheckFail,
Flags: []cli.Flag{
&gitHubTokenFlag,
},
},
},
},
}
app.Commands = append(app.Commands, additionalCommands...)

View File

@@ -1,80 +0,0 @@
package main
import (
"context"
"log"
"strings"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/gpg"
"github.com/grafana/grafana/pkg/build/packaging"
"github.com/grafana/grafana/pkg/build/syncutil"
)
func Package(c *cli.Context) error {
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
edition := config.Edition(c.String("edition"))
releaseMode, err := metadata.GetReleaseMode()
if err != nil {
return cli.Exit(err.Error(), 1)
}
releaseModeConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
if err != nil {
return cli.Exit(err.Error(), 1)
}
cfg := config.Config{
NumWorkers: c.Int("jobs"),
SignPackages: c.Bool("sign"),
}
ctx := context.Background()
variants := []config.Variant{}
variantStrs := strings.Split(c.String("variants"), ",")
if c.String("variants") != "" {
for _, varStr := range variantStrs {
if varStr == "" {
continue
}
variants = append(variants, config.Variant(varStr))
}
} else {
variants = releaseModeConfig.Variants
}
if len(variants) == 0 {
variants = config.AllVariants
}
log.Printf("Packaging Grafana version %q, version mode %s, %s edition, variants %s", metadata.GrafanaVersion, releaseMode.Mode,
edition, strings.Join(variantStrs, ","))
if cfg.SignPackages {
if err := gpg.LoadGPGKeys(&cfg); err != nil {
return cli.Exit(err, 1)
}
defer gpg.RemoveGPGFiles(cfg)
if err := gpg.Import(cfg); err != nil {
return cli.Exit(err, 1)
}
}
p := syncutil.NewWorkerPool(cfg.NumWorkers)
defer p.Close()
if err := packaging.PackageGrafana(ctx, metadata.GrafanaVersion, ".", cfg, edition, variants, releaseModeConfig.PluginSignature.Sign, p); err != nil {
return cli.Exit(err, 1)
}
log.Println("Successfully packaged Grafana!")
return nil
}

View File

@@ -1,101 +0,0 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/docker"
"github.com/grafana/grafana/pkg/build/gcloud"
)
func Enterprise2(c *cli.Context) error {
if c.NArg() > 0 {
if err := cli.ShowSubcommandHelp(c); err != nil {
return cli.Exit(err.Error(), 1)
}
return cli.Exit("", 1)
}
if err := gcloud.ActivateServiceAccount(); err != nil {
return fmt.Errorf("couldn't activate service account, err: %w", err)
}
metadata, err := config.GenerateMetadata(c)
if err != nil {
return err
}
buildConfig, err := config.GetBuildConfig(metadata.ReleaseMode.Mode)
if err != nil {
return err
}
cfg := docker.Config{
Archs: buildConfig.Docker.Architectures,
Distribution: buildConfig.Docker.Distribution,
DockerHubRepo: c.String("dockerhub-repo"),
Tag: metadata.GrafanaVersion,
}
err = dockerLoginEnterprise2()
if err != nil {
return err
}
var distributionStr []string
for _, distribution := range cfg.Distribution {
switch distribution {
case alpine:
distributionStr = append(distributionStr, "")
case ubuntu:
distributionStr = append(distributionStr, "-ubuntu")
default:
return fmt.Errorf("unrecognized distribution %q", distribution)
}
}
for _, distribution := range distributionStr {
var imageFileNames []string
for _, arch := range cfg.Archs {
imageFilename := fmt.Sprintf("%s:%s%s-%s", cfg.DockerHubRepo, cfg.Tag, distribution, arch)
err := docker.PushImage(imageFilename)
if err != nil {
return err
}
imageFileNames = append(imageFileNames, imageFilename)
}
manifest := fmt.Sprintf("%s:%s%s", cfg.DockerHubRepo, cfg.Tag, distribution)
args := []string{"manifest", "create", manifest}
args = append(args, imageFileNames...)
//nolint:gosec
cmd := exec.Command("docker", args...)
cmd.Env = append(os.Environ(), "DOCKER_CLI_EXPERIMENTAL=enabled")
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create Docker manifest: %w\n%s", err, output)
}
err = docker.PushManifest(manifest)
if err != nil {
return err
}
}
return nil
}
func dockerLoginEnterprise2() error {
log.Println("Docker login...")
cmd := exec.Command("gcloud", "auth", "configure-docker")
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("error logging in to DockerHub: %s %q", out, err)
}
log.Println("Successful login!")
return nil
}

View File

@@ -1,42 +0,0 @@
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/urfave/cli/v2"
)
func Shellcheck(c *cli.Context) error {
log.Println("Running shellcheck...")
fpaths := []string{}
if err := filepath.Walk("scripts", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if strings.HasSuffix(path, ".sh") {
fpaths = append(fpaths, path)
}
return nil
}); err != nil {
return fmt.Errorf("couldn't traverse scripts/: %w", err)
}
log.Printf("Running shellcheck on %s", strings.Join(fpaths, ","))
args := append([]string{"-e", "SC1071", "-e", "SC2162"}, fpaths...)
//nolint:gosec
cmd := exec.Command("shellcheck", args...)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("shellcheck failed: %s", output)
}
log.Println("Successfully ran shellcheck!")
return nil
}

View File

@@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/urfave/cli/v2"
@@ -14,9 +15,28 @@ import (
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/droneutil"
"github.com/grafana/grafana/pkg/build/gcloud"
"github.com/grafana/grafana/pkg/build/packaging"
)
// PackageRegexp returns a regexp for matching packages corresponding to a certain Grafana edition.
func PackageRegexp(edition config.Edition) *regexp.Regexp {
var sfx string
switch edition {
case config.EditionOSS:
case config.EditionEnterprise:
sfx = "-enterprise"
case config.EditionEnterprise2:
sfx = "-enterprise2"
default:
panic(fmt.Sprintf("unrecognized edition %q", edition))
}
rePkg, err := regexp.Compile(fmt.Sprintf(`^grafana%s(?:-rpi)?[-_][^-_]+.*$`, sfx))
if err != nil {
panic(fmt.Sprintf("Failed to compile regexp: %s", err))
}
return rePkg
}
const releaseFolder = "release"
const mainFolder = "main"
const releaseBranchFolder = "prerelease"
@@ -181,7 +201,7 @@ func uploadPackages(cfg uploadConfig) error {
return fmt.Errorf("failed to list packages: %w", err)
}
fpaths := []string{}
rePkg := packaging.PackageRegexp(cfg.edition)
rePkg := PackageRegexp(cfg.edition)
for _, fpath := range matches {
fname := filepath.Base(fpath)
if strings.Contains(fname, "latest") || !rePkg.MatchString(fname) {

View File

@@ -1,142 +0,0 @@
package main
import (
"context"
"errors"
"fmt"
"io/fs"
"os/exec"
"path/filepath"
"strings"
"github.com/urfave/cli/v2"
)
func mapSlice[I any, O any](a []I, f func(I) O) []O {
o := make([]O, len(a))
for i, e := range a {
o[i] = f(e)
}
return o
}
// VerifyStarlark is the CLI Action for verifying Starlark files in a workspace.
// It expects a single context argument which is the path to the workspace.
// The actual verification procedure can return multiple errors which are
// joined together to be one holistic error for the action.
func VerifyStarlark(c *cli.Context) error {
if c.NArg() != 1 {
var message string
if c.NArg() == 0 {
message = "ERROR: missing required argument <workspace path>"
}
if c.NArg() > 1 {
message = "ERROR: too many arguments"
}
if err := cli.ShowSubcommandHelp(c); err != nil {
return err
}
return cli.Exit(message, 1)
}
workspace := c.Args().Get(0)
verificationErrs, executionErr := verifyStarlark(c.Context, workspace, buildifierLintCommand)
if executionErr != nil {
return executionErr
}
if len(verificationErrs) == 0 {
return nil
}
noun := "file"
if len(verificationErrs) > 1 {
noun += "s"
}
return fmt.Errorf("verification failed for %d %s:\n%s",
len(verificationErrs),
noun,
strings.Join(
mapSlice(verificationErrs, func(e error) string { return e.Error() }),
"\n",
))
}
type commandFunc = func(path string) (command string, args []string)
func buildifierLintCommand(path string) (string, []string) {
return "buildifier", []string{"-lint", "warn", "-mode", "check", path}
}
// verifyStarlark walks all directories starting at provided workspace path and
// verifies any Starlark files it finds.
// Starlark files are assumed to end with the .star extension.
// The verification relies on linting frovided by the 'buildifier' binary which
// must be in the PATH.
// A slice of verification errors are returned, one for each file that failed verification.
// If any execution of the `buildifier` command fails, this is returned separately.
// commandFn is executed on every Starlark file to determine the command and arguments to be executed.
// The caller is trusted and it is the callers responsibility to ensure that the resulting command is safe to execute.
func verifyStarlark(ctx context.Context, workspace string, commandFn commandFunc) ([]error, error) {
var verificationErrs []error
// All errors from filepath.WalkDir are filtered by the fs.WalkDirFunc.
// Lstat or ReadDir errors are reported as verificationErrors.
// If any execution of the `buildifier` command fails or if the context is cancelled,
// it is reported as an error and any verification of subsequent files is skipped.
err := filepath.WalkDir(workspace, func(path string, d fs.DirEntry, err error) error {
// Skip verification of the file or files within the directory if there is an error
// returned by Lstat or ReadDir.
if err != nil {
verificationErrs = append(verificationErrs, err)
return nil
}
if d.IsDir() {
return nil
}
if filepath.Ext(path) == ".star" {
command, args := commandFn(path)
// The caller is trusted.
//nolint:gosec
cmd := exec.CommandContext(ctx, command, args...)
cmd.Dir = workspace
_, err = cmd.Output()
if err == nil { // No error, early return.
return nil
}
// The error returned from cmd.Output() is never wrapped.
//nolint:errorlint
if err, ok := err.(*exec.ExitError); ok {
switch err.ExitCode() {
// Case comments are informed by the output of `buildifier --help`
case 1: // syntax errors in input
verificationErrs = append(verificationErrs, errors.New(string(err.Stderr)))
return nil
case 2: // usage errors: invoked incorrectly
return fmt.Errorf("command %q: %s", cmd, err.Stderr)
case 3: // unexpected runtime errors: file I/O problems or internal bugs
return fmt.Errorf("command %q: %s", cmd, err.Stderr)
case 4: // check mode failed (reformat is needed)
verificationErrs = append(verificationErrs, errors.New(string(err.Stderr)))
return nil
default:
return fmt.Errorf("command %q: %s", cmd, err.Stderr)
}
}
// Error was not an exit error from the command.
return fmt.Errorf("command %q: %v", cmd, err)
}
return nil
})
return verificationErrs, err
}

View File

@@ -1,137 +0,0 @@
//go:build requires_buildifier
package main
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestVerifyStarlark(t *testing.T) {
t.Run("execution errors", func(t *testing.T) {
t.Run("invalid usage", func(t *testing.T) {
ctx := context.Background()
workspace := t.TempDir()
err := os.WriteFile(filepath.Join(workspace, "ignored.star"), []byte{}, os.ModePerm)
if err != nil {
t.Fatalf(err.Error())
}
_, executionErr := verifyStarlark(ctx, workspace, func(string) (string, []string) { return "buildifier", []string{"--invalid"} })
if executionErr == nil {
t.Fatalf("Expected execution error but got none")
}
})
t.Run("context cancellation", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
workspace := t.TempDir()
err := os.WriteFile(filepath.Join(workspace, "ignored.star"), []byte{}, os.ModePerm)
if err != nil {
t.Fatalf(err.Error())
}
err = os.WriteFile(filepath.Join(workspace, "other-ignored.star"), []byte{}, os.ModePerm)
if err != nil {
t.Fatalf(err.Error())
}
cancel()
_, executionErr := verifyStarlark(ctx, workspace, buildifierLintCommand)
if executionErr == nil {
t.Fatalf("Expected execution error but got none")
}
})
})
t.Run("verification errors", func(t *testing.T) {
t.Run("a single file with lint", func(t *testing.T) {
ctx := context.Background()
workspace := t.TempDir()
invalidContent := []byte(`load("scripts/drone/other.star", "function")
function()`)
err := os.WriteFile(filepath.Join(workspace, "has-lint.star"), invalidContent, os.ModePerm)
if err != nil {
t.Fatalf(err.Error())
}
verificationErrs, executionErr := verifyStarlark(ctx, workspace, buildifierLintCommand)
if executionErr != nil {
t.Fatalf("Unexpected execution error: %v", executionErr)
}
if len(verificationErrs) == 0 {
t.Fatalf(`"has-lint.star" requires linting but the verifyStarlark function provided no linting error`)
}
if len(verificationErrs) > 1 {
t.Fatalf(`verifyStarlark returned multiple errors for the "has-lint.star" file but only one was expected: %v`, verificationErrs)
}
if !strings.Contains(verificationErrs[0].Error(), "has-lint.star:1: module-docstring: The file has no module docstring.") {
t.Fatalf(`"has-lint.star" is missing a module docstring but the verifyStarlark function linting error did not mention this, instead we got: %v`, verificationErrs[0])
}
})
t.Run("no files with lint", func(t *testing.T) {
ctx := context.Background()
workspace := t.TempDir()
content := []byte(`"""
This module does nothing.
"""
load("scripts/drone/other.star", "function")
function()
`)
require.NoError(t, os.WriteFile(filepath.Join(workspace, "no-lint.star"), content, os.ModePerm))
verificationErrs, executionErr := verifyStarlark(ctx, workspace, buildifierLintCommand)
if executionErr != nil {
t.Fatalf("Unexpected execution error: %v", executionErr)
}
if len(verificationErrs) != 0 {
t.Log(`"no-lint.star" has no lint but the verifyStarlark function provided at least one error`)
for _, err := range verificationErrs {
t.Log(err)
}
t.FailNow()
}
})
t.Run("multiple files with lint", func(t *testing.T) {
ctx := context.Background()
workspace := t.TempDir()
invalidContent := []byte(`load("scripts/drone/other.star", "function")
function()`)
require.NoError(t, os.WriteFile(filepath.Join(workspace, "has-lint.star"), invalidContent, os.ModePerm))
require.NoError(t, os.WriteFile(filepath.Join(workspace, "has-lint2.star"), invalidContent, os.ModePerm))
verificationErrs, executionErr := verifyStarlark(ctx, workspace, buildifierLintCommand)
if executionErr != nil {
t.Fatalf("Unexpected execution error: %v", executionErr)
}
if len(verificationErrs) == 0 {
t.Fatalf(`Two files require linting but the verifyStarlark function provided no linting error`)
}
if len(verificationErrs) == 1 {
t.Fatalf(`Two files require linting but the verifyStarlark function provided only one linting error: %v`, verificationErrs[0])
}
if len(verificationErrs) > 2 {
t.Fatalf(`verifyStarlark returned more errors than expected: %v`, verificationErrs)
}
if !strings.Contains(verificationErrs[0].Error(), "has-lint.star:1: module-docstring: The file has no module docstring.") {
t.Errorf(`"has-lint.star" is missing a module docstring but the verifyStarlark function linting error did not mention this, instead we got: %v`, verificationErrs[0])
}
if !strings.Contains(verificationErrs[1].Error(), "has-lint2.star:1: module-docstring: The file has no module docstring.") {
t.Fatalf(`"has-lint2.star" is missing a module docstring but the verifyStarlark function linting error did not mention this, instead we got: %v`, verificationErrs[0])
}
})
})
}

View File

@@ -1,50 +0,0 @@
package compilers
import (
"fmt"
"os"
"os/exec"
"path/filepath"
)
const (
ArmV6 = "/opt/rpi-tools/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc"
Armv7 = "arm-linux-gnueabihf-gcc"
Armv7Musl = "/tmp/arm-linux-musleabihf-cross/bin/arm-linux-musleabihf-gcc"
Arm64 = "aarch64-linux-gnu-gcc"
Arm64Musl = "/tmp/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc"
Osx64 = "/tmp/osxcross/target/bin/o64-clang"
Win64 = "x86_64-w64-mingw32-gcc"
LinuxX64 = "/tmp/x86_64-centos6-linux-gnu/bin/x86_64-centos6-linux-gnu-gcc"
LinuxX64Musl = "/tmp/x86_64-linux-musl-cross/bin/x86_64-linux-musl-gcc"
)
func Install() error {
// From the os.TempDir documentation:
// On Unix systems, it returns $TMPDIR if non-empty,
// else /tmp. On Windows, it uses GetTempPath,
// returning the first non-empty value from %TMP%, %TEMP%, %USERPROFILE%,
// or the Windows directory. On Plan 9, it returns /tmp.
tmp := os.TempDir()
var (
centosArchive = "x86_64-centos6-linux-gnu.tar.xz"
osxArchive = "osxcross.tar.xz"
)
for _, fname := range []string{centosArchive, osxArchive} {
path := filepath.Join(tmp, fname)
if _, err := os.Stat(path); err != nil {
return fmt.Errorf("stat error: %w", err)
}
// Ignore gosec G204 as this function is only used in the build process.
//nolint:gosec
cmd := exec.Command("tar", "xfJ", fname)
cmd.Dir = tmp
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to unpack %q: %q, %w", fname, output, err)
}
}
return nil
}

View File

@@ -1,69 +0,0 @@
package config
import (
"context"
"fmt"
"log"
"strconv"
"time"
"github.com/grafana/grafana/pkg/build/executil"
)
type Revision struct {
Timestamp int64
SHA256 string
EnterpriseCommit string
Branch string
}
func GrafanaTimestamp(ctx context.Context, dir string) (int64, error) {
out, err := executil.OutputAt(ctx, dir, "git", "show", "-s", "--format=%ct")
if err != nil {
return time.Now().Unix(), nil
}
stamp, err := strconv.ParseInt(out, 10, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse output from git show: %q", out)
}
return stamp, nil
}
// GrafanaRevision uses git commands to get information about the checked out Grafana code located at 'grafanaDir'.
// This could maybe be a more generic "Describe" function in the "git" package.
func GrafanaRevision(ctx context.Context, grafanaDir string) (Revision, error) {
stamp, err := GrafanaTimestamp(ctx, grafanaDir)
if err != nil {
return Revision{}, err
}
sha, err := executil.OutputAt(ctx, grafanaDir, "git", "rev-parse", "--short", "HEAD")
if err != nil {
return Revision{}, err
}
enterpriseCommit, err := executil.OutputAt(ctx, grafanaDir, "git", "-C", "../grafana-enterprise", "rev-parse", "--short", "HEAD")
if err != nil {
enterpriseCommit, err = executil.OutputAt(ctx, grafanaDir, "git", "-C", "..", "rev-parse", "--short", "HEAD")
if err != nil {
enterpriseCommit, err = executil.OutputAt(ctx, grafanaDir, "git", "-C", "/tmp/grafana-enterprise", "rev-parse", "--short", "HEAD")
if err != nil {
log.Println("Could not get enterprise commit. Error:", err)
}
}
}
branch, err := executil.OutputAt(ctx, grafanaDir, "git", "rev-parse", "--abbrev-ref", "HEAD")
if err != nil {
return Revision{}, err
}
return Revision{
SHA256: sha,
EnterpriseCommit: enterpriseCommit,
Branch: branch,
Timestamp: stamp,
}, nil
}

View File

@@ -1,35 +0,0 @@
package cryptoutil
import (
"crypto/md5"
"fmt"
"io"
"log"
"os"
)
func MD5File(fpath string) error {
// Ignore gosec G304 as this function is only used in the build process.
//nolint:gosec
fd, err := os.Open(fpath)
if err != nil {
return err
}
defer func() {
if err := fd.Close(); err != nil {
log.Printf("error closing file at '%s': %s", fpath, err.Error())
}
}()
h := md5.New() // nolint:gosec
if _, err = io.Copy(h, fd); err != nil {
return err
}
// nolint:gosec
if err := os.WriteFile(fpath+".md5", []byte(fmt.Sprintf("%x\n", h.Sum(nil))), 0664); err != nil {
return err
}
return nil
}

View File

@@ -1,182 +0,0 @@
package docker
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/grafana/grafana/pkg/build/config"
)
// verifyArchive verifies the integrity of an archive file.
func verifyArchive(archive string) error {
log.Printf("Verifying checksum of %q", archive)
//nolint:gosec
shaB, err := os.ReadFile(archive + ".sha256")
if err != nil {
return err
}
exp := strings.TrimSpace(string(shaB))
//nolint:gosec
f, err := os.Open(archive)
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
log.Println("error closing file:", err)
}
}()
h := sha256.New()
_, err = io.Copy(h, f)
if err != nil {
return err
}
chksum := hex.EncodeToString(h.Sum(nil))
if chksum != exp {
return fmt.Errorf("archive checksum is different than expected: %q", archive)
}
log.Printf("Archive %q has expected checksum: %s", archive, exp)
return nil
}
// BuildImage builds a Docker image.
// The image tag is returned.
func BuildImage(version string, arch config.Architecture, grafanaDir string, useUbuntu, shouldSave bool, edition config.Edition, mode config.VersionMode) ([]string, error) {
var baseArch string
switch arch {
case "amd64":
case "armv7":
baseArch = "arm32v7/"
case "arm64":
baseArch = "arm64v8/"
default:
return []string{}, fmt.Errorf("unrecognized architecture %q", arch)
}
libc := "-musl"
baseImage := fmt.Sprintf("%salpine:3.18.5", baseArch)
tagSuffix := ""
if useUbuntu {
libc = ""
baseImage = fmt.Sprintf("%subuntu:22.04", baseArch)
tagSuffix = "-ubuntu"
}
var editionStr string
var dockerRepo string
var additionalDockerRepo string
var tags []string
var imageFileBase string
var dockerEnterprise2Repo string
if repo, ok := os.LookupEnv("DOCKER_ENTERPRISE2_REPO"); ok {
dockerEnterprise2Repo = repo
}
switch edition {
case config.EditionOSS:
dockerRepo = "grafana/grafana-image-tags"
additionalDockerRepo = "grafana/grafana-oss-image-tags"
imageFileBase = "grafana-oss"
case config.EditionEnterprise:
dockerRepo = "grafana/grafana-enterprise-image-tags"
imageFileBase = "grafana-enterprise"
editionStr = "-enterprise"
case config.EditionEnterprise2:
dockerRepo = dockerEnterprise2Repo
imageFileBase = "grafana-enterprise2"
editionStr = "-enterprise2"
default:
return []string{}, fmt.Errorf("unrecognized edition %s", edition)
}
buildDir := filepath.Join(grafanaDir, "packaging/docker")
// For example: grafana-8.5.0-52819pre.linux-amd64-musl.tar.gz
archive := fmt.Sprintf("grafana%s-%s.linux-%s%s.tar.gz", editionStr, version, arch, libc)
if err := verifyArchive(filepath.Join(buildDir, archive)); err != nil {
return []string{}, err
}
tag := fmt.Sprintf("%s:%s%s-%s", dockerRepo, version, tagSuffix, arch)
tags = append(tags, tag)
args := []string{
"build",
"-q",
"--build-arg", fmt.Sprintf("BASE_IMAGE=%s", baseImage),
"--build-arg", fmt.Sprintf("GRAFANA_TGZ=%s", archive),
"--build-arg", "GO_SRC=tgz-builder",
"--build-arg", "JS_SRC=tgz-builder",
"--build-arg", "RUN_SH=./run.sh",
"--tag", tag,
"--no-cache",
"--file", "../../Dockerfile",
".",
"--label", fmt.Sprintf("mode=%s", string(mode)),
}
//nolint:gosec
cmd := exec.Command("docker", args...)
cmd.Dir = buildDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), "DOCKER_CLI_EXPERIMENTAL=enabled", "DOCKER_BUILDKIT=1")
log.Printf("Running Docker: DOCKER_CLI_EXPERIMENTAL=enabled DOCKER_BUILDKIT=1 %s", cmd)
if err := cmd.Run(); err != nil {
return []string{}, fmt.Errorf("building Docker image failed: %w", err)
}
if shouldSave {
imageFile := fmt.Sprintf("%s-%s%s-%s.img", imageFileBase, version, tagSuffix, arch)
//nolint:gosec
cmd = exec.Command("docker", "save", tag, "-o", imageFile)
cmd.Dir = buildDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Printf("Running Docker: %s", cmd)
if err := cmd.Run(); err != nil {
return []string{}, fmt.Errorf("saving Docker image failed: %w", err)
}
gcsURL := fmt.Sprintf("gs://grafana-prerelease/artifacts/docker/%s/%s", version, imageFile)
//nolint:gosec
cmd = exec.Command("gsutil", "-q", "cp", imageFile, gcsURL)
cmd.Dir = buildDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Printf("Running gsutil: %s", cmd)
if err := cmd.Run(); err != nil {
return []string{}, fmt.Errorf("storing Docker image failed: %w", err)
}
log.Printf("Docker image %s stored to grafana-prerelease GCS bucket", imageFile)
}
if additionalDockerRepo != "" {
additionalTag := fmt.Sprintf("%s:%s%s-%s", additionalDockerRepo, version, tagSuffix, arch)
//nolint:gosec
cmd = exec.Command("docker", "tag", tag, additionalTag)
cmd.Dir = buildDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Printf("Running Docker: %s", cmd)
if err := cmd.Run(); err != nil {
return []string{}, fmt.Errorf("tagging Docker image failed: %w", err)
}
tags = append(tags, additionalTag)
}
return tags, nil
}

View File

@@ -1,34 +0,0 @@
package docker
import (
"fmt"
"log"
"os"
"os/exec"
)
// AllArchs is a list of all supported Docker image architectures.
var AllArchs = []string{"amd64", "arm64"}
// emulatorImage is the docker image used as the cross-platform emulator
var emulatorImage = "tonistiigi/binfmt:qemu-v7.0.0"
// Init initializes the OS for Docker image building.
func Init() error {
// Necessary for cross-platform builds
if err := os.Setenv("DOCKER_BUILDKIT", "1"); err != nil {
log.Println("error setting DOCKER_BUILDKIT environment variable:", err)
}
// Enable execution of Docker images for other architectures
//nolint:gosec
cmd := exec.Command("docker", "run", "--privileged", "--rm",
emulatorImage, "--install", "all")
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("failed to enable execution of cross-platform Docker images: %w\n%s", err, output)
}
log.Println("emulators have been installed successfully!")
return nil
}

View File

@@ -1,62 +0,0 @@
package docker
import (
"fmt"
"log"
"os"
"os/exec"
"time"
)
const (
tries = 3
sleepTime = 30
)
func PushImage(newImage string) error {
var err error
for i := 0; i < tries; i++ {
log.Printf("push attempt #%d...", i+1)
var out []byte
cmd := exec.Command("docker", "push", newImage)
cmd.Dir = "."
out, err = cmd.CombinedOutput()
if err != nil {
log.Printf("output: %s", out)
log.Printf("sleep for %d, before retrying...", sleepTime)
time.Sleep(sleepTime * time.Second)
} else {
log.Printf("Successfully pushed %s!", newImage)
break
}
}
if err != nil {
return fmt.Errorf("error pushing images to DockerHub: %q", err)
}
return nil
}
func PushManifest(manifest string) error {
log.Printf("Pushing Docker manifest %s...", manifest)
var err error
for i := 0; i < tries; i++ {
log.Printf("push attempt #%d...", i+1)
var out []byte
cmd := exec.Command("docker", "manifest", "push", manifest)
cmd.Env = append(os.Environ(), "DOCKER_CLI_EXPERIMENTAL=enabled")
out, err = cmd.CombinedOutput()
if err != nil {
log.Printf("output: %s", out)
log.Printf("sleep for %d, before retrying...", sleepTime)
time.Sleep(sleepTime * time.Second)
} else {
log.Printf("Successful manifest push! %s", string(out))
break
}
}
if err != nil {
return fmt.Errorf("failed to push manifest, err: %w", err)
}
return nil
}

View File

@@ -1,18 +0,0 @@
package env
import (
"strings"
)
// Lookup is the equivalent of os.LookupEnv, only you are able to provide the list of environment variables.
// To use this as os.LookupEnv would be used, simply call
// `env.Lookup("ENVIRONMENT_VARIABLE", os.Environ())`
func Lookup(name string, vars []string) (string, bool) {
for _, v := range vars {
if strings.HasPrefix(v, name) {
return strings.TrimPrefix(v, name+"="), true
}
}
return "", false
}

View File

@@ -1,43 +0,0 @@
package env_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/build/env"
)
func TestLookup(t *testing.T) {
values := []string{"ENV_1=a", "ENV_2=b", "ENV_3=c", "ENV_4_TEST="}
{
v, ok := env.Lookup("ENV_1", values)
require.Equal(t, v, "a")
require.True(t, ok)
}
{
v, ok := env.Lookup("ENV_2", values)
require.Equal(t, v, "b")
require.True(t, ok)
}
{
v, ok := env.Lookup("ENV_3", values)
require.Equal(t, v, "c")
require.True(t, ok)
}
{
v, ok := env.Lookup("ENV_4_TEST", values)
require.Equal(t, v, "")
require.True(t, ok)
}
{
v, ok := env.Lookup("NOT_THERE", values)
require.Equal(t, v, "")
require.False(t, ok)
}
}

View File

@@ -1,61 +0,0 @@
package errutil
import (
"context"
"log"
"sync"
)
type Group struct {
cancel func()
wg sync.WaitGroup
errOnce sync.Once
err error
}
func GroupWithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}
// Wait waits for any wrapped goroutines to finish and returns any error having occurred in one of them.
func (g *Group) Wait() error {
log.Println("Waiting on Group")
g.wg.Wait()
if g.cancel != nil {
log.Println("Group canceling its context after waiting")
g.cancel()
}
return g.err
}
// Cancel cancels the associated context.
func (g *Group) Cancel() {
log.Println("Group's Cancel method being called")
g.cancel()
}
// Wrap wraps a function to be executed in a goroutine.
func (g *Group) Wrap(f func() error) func() {
g.wg.Add(1)
return func() {
defer g.wg.Done()
if err := f(); err != nil {
g.errOnce.Do(func() {
log.Printf("An error occurred in Group: %s", err)
g.err = err
if g.cancel != nil {
log.Println("Group canceling its context due to error")
g.cancel()
}
})
}
}
}
// Go wraps the provided function and executes it in a goroutine.
func (g *Group) Go(f func() error) {
wrapped := g.Wrap(f)
go wrapped()
}

View File

@@ -1,46 +0,0 @@
package executil
import (
"context"
"fmt"
"os/exec"
"strings"
)
func RunAt(ctx context.Context, dir, cmd string, args ...string) error {
// Ignore gosec G204 as this function is only used in the build process.
//nolint:gosec
c := exec.CommandContext(ctx, cmd, args...)
c.Dir = dir
b, err := c.CombinedOutput()
if err != nil {
return fmt.Errorf("%w. '%s %v': %s", err, cmd, args, string(b))
}
return nil
}
func Run(ctx context.Context, cmd string, args ...string) error {
return RunAt(ctx, ".", cmd, args...)
}
func OutputAt(ctx context.Context, dir, cmd string, args ...string) (string, error) {
// Ignore gosec G204 as this function is only used in the build process.
//nolint:gosec
c := exec.CommandContext(ctx, cmd, args...)
c.Dir = dir
b, err := c.CombinedOutput()
if err != nil {
return "", err
}
return strings.TrimSpace(string(b)), nil
}
func Output(ctx context.Context, cmd string, args ...string) (string, error) {
return OutputAt(ctx, ".", cmd, args...)
}

View File

@@ -1,56 +0,0 @@
package frontend
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"github.com/grafana/grafana/pkg/build/config"
"github.com/grafana/grafana/pkg/build/errutil"
"github.com/grafana/grafana/pkg/build/lerna"
"github.com/grafana/grafana/pkg/build/syncutil"
)
func BuildFrontendPackages(version string, edition config.Edition, grafanaDir string, p syncutil.WorkerPool, g *errutil.Group) error {
p.Schedule(g.Wrap(func() error {
if err := lerna.BuildFrontendPackages(version, edition, grafanaDir); err != nil {
return fmt.Errorf("failed to build %s frontend packages: %v", edition, err)
}
log.Printf("Finished building %s frontend packages", string(edition))
return nil
}))
return nil
}
// Build builds the Grafana front-end
func Build(edition config.Edition, grafanaDir string, p syncutil.WorkerPool, g *errutil.Group) error {
log.Printf("Building %s frontend in %q", edition, grafanaDir)
grafanaDir, err := filepath.Abs(grafanaDir)
if err != nil {
return err
}
for _, dpath := range []string{"tmp", "public_gen", "public/build"} {
dpath = filepath.Join(grafanaDir, dpath)
if err := os.RemoveAll(dpath); err != nil {
return fmt.Errorf("failed to remove %q: %w", dpath, err)
}
}
p.Schedule(g.Wrap(func() error {
cmd := exec.Command("yarn", "run", "build")
cmd.Dir = grafanaDir
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to build %s frontend with webpack: %s", edition, output)
}
log.Printf("Finished building %s frontend", edition)
return nil
}))
return nil
}

Some files were not shown because too many files have changed in this diff Show More