Merge branch 'master' into docs-update

pull/2581/head
Matt Butcher 8 years ago committed by GitHub
commit 3437a578c3

@ -33,21 +33,21 @@ Think of it like apt/yum/homebrew for Kubernetes.
Binary downloads of the Helm client can be found at the following links:
- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.4.2-darwin-amd64.tar.gz)
- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.4.2-linux-amd64.tar.gz)
- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.4.2-linux-386.tar.gz)
- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.5.0-darwin-amd64.tar.gz)
- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.5.0-linux-amd64.tar.gz)
- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.5.0-linux-386.tar.gz)
Unpack the `helm` binary and add it to your PATH and you are good to go!
macOS/[homebrew](https://brew.sh/) users can also use `brew install kubernetes-helm`.
To rapidly get Helm up and running, start with the [Quick Start Guide](docs/quickstart.md).
To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide).
See the [installation guide](docs/install.md) for more options,
See the [installation guide](https://docs.helm.sh/using_helm/#installing-helm) for more options,
including installing pre-releases.
## Docs
Get started with the [Quick Start guide](docs/quickstart.md) or plunge into the [complete documentation](docs/index.md)
Get started with the [Quick Start guide](https://docs.helm.sh/using_helm/#quickstart-guide) or plunge into the [complete documentation](https://docs.helm.sh)
## Roadmap

@ -273,6 +273,7 @@ func ensureDirectories(home helmpath.Home, out io.Writer) error {
home.LocalRepository(),
home.Plugins(),
home.Starters(),
home.Archive(),
}
for _, p := range configDirectories {
if fi, err := os.Stat(p); err != nil {

@ -399,7 +399,11 @@ func locateChartPath(repoURL, name, version string, verify bool, keyring,
name = chartURL
}
filename, _, err := dl.DownloadTo(name, version, ".")
if _, err := os.Stat(settings.Home.Archive()); os.IsNotExist(err) {
os.MkdirAll(settings.Home.Archive(), 0744)
}
filename, _, err := dl.DownloadTo(name, version, settings.Home.Archive())
if err == nil {
lname, err := filepath.Abs(filename)
if err != nil {

@ -27,6 +27,6 @@ Name|Status|Description
-----|------|----------
heritage | REC | This should always be set to `{{ .Release.Service }}`. It is for finding all things managed by Tiller.
release | REC | This should be the `{{ .Release.Name }}`.
chart | REC | This should be the chart name and version: `{{ .Chart.Name }}-{{ .Chart.Version }}`.
app | REC | This should usually be set to `{{ template "name" . }}`, reflecting the entire app. This is used by many Kubernetes manifests, and is not Helm-specific.
chart | REC | This should be the chart name and version: `{{ .Chart.Name }}-{{ .Chart.Version \| replace "+" "_" }}`.
app | OPT | This should be the app name, reflecting the entire app. Often the chart's `{{ .Chart.Name }}` is used for this. This is used by many Kubernetes manifests, and is not Helm-specific.
component | OPT | This is a common label for marking the different roles that pieces may play in an application. For example, `component: frontend`

@ -11,7 +11,7 @@ send us a pull request.
**Q: Why do I get a `unsupported protocol scheme ""` error when trying to fetch a chart from my custom repo?**
A: (Helm < 2.5.0) This is likely caused by you creating your chart repo index without specifying the `--url` flag.
Try recreating your `index.yaml` file with a command like `heml repo index --url http://my-repo/charts .`,
Try recreating your `index.yaml` file with a command like `helm repo index --url http://my-repo/charts .`,
and then re-uploading it to your custom charts repo.
This behavior was changed in Helm 2.5.0.

@ -1,7 +1,7 @@
name: alpine
description: Deploy a basic Alpine Linux pod
version: 0.1.0
home: https://k8s.io/helm
home: https://github.com/kubernetes/helm
sources:
- https://github.com/kubernetes/helm
appVersion: 3.3

@ -2,8 +2,6 @@
Run a single pod of Alpine Linux.
This example was generated using the command `helm create alpine`.
The `templates/` directory contains a very simple pod resource with a
couple of parameters.

@ -0,0 +1,16 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

@ -1,26 +1,23 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{.Release.Name}}-{{.Values.Name}}"
name: {{ template "fullname" . }}
labels:
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
# is responsible for.
heritage: {{.Release.Service | quote }}
heritage: {{ .Release.Service }}
# The "release" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
release: {{.Release.Name | quote }}
release: {{ .Release.Name }}
# This makes it easy to audit chart usage.
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
annotations:
"helm.sh/created": {{.Release.Time.Seconds | quote }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
spec:
# This shows how to use a simple value. This will look for a passed-in value
# called restartPolicy. If it is not found, it will use the default value.
# {{default "Never" .restartPolicy}} is a slightly optimized version of the
# more conventional syntax: {{.restartPolicy | default "Never"}}
restartPolicy: {{default "Never" .Values.restartPolicy}}
# This shows how to use a simple value. This will look for a passed-in value called restartPolicy.
restartPolicy: {{ .Values.restartPolicy }}
containers:
- name: waiter
image: "alpine:3.3"
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/bin/sleep", "9000"]

@ -1,2 +1,6 @@
# The pod name
Name: my-alpine
image:
repository: alpine
tag: 3.3
pullPolicy: IfNotPresent
restartPolicy: Never

@ -6,9 +6,9 @@ keywords:
- nginx
- www
- web
home: "https://github.com/kubernetes/helm"
home: https://github.com/kubernetes/helm
sources:
- "https://hub.docker.com/_/nginx/"
- https://hub.docker.com/_/nginx/
maintainers:
- name: technosophos
email: mbutcher@deis.com

@ -13,7 +13,7 @@ pattern:
- A `Deployment` is used to create a Replica Set of nginx pods.
([templates/deployment.yaml](templates/deployment.yaml))
- A `Service` is used to create a gateway to the pods running in the
replica set ([templates/svc.yaml](templates/svc.yaml))
replica set ([templates/svc.yaml](templates/service.yaml))
The [values.yaml](values.yaml) exposes a few of the configuration options in the
charts, though there are some that are not exposed there (like

@ -2,15 +2,15 @@
{{/*
Expand the name of the chart.
*/}}
{{define "name"}}{{default "nginx" .Values.nameOverride | trunc 63 | trimSuffix "-" }}{{end}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this
(by the DNS naming spec).
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{define "fullname"}}
{{- $name := default "nginx" .Values.nameOverride -}}
{{printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{end}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

@ -1,15 +1,14 @@
# This is a simple example of using a config map to create a single page
# static site.
# This is a simple example of using a config map to create a single page static site.
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "fullname" . }}
labels:
release: {{ .Release.Name | quote }}
app: {{template "fullname" .}}
heritage: {{.Release.Service | quote }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
data:
# When the config map is mounted as a volume, these will be created as
# files.
index.html: {{default "Hello" .Values.index | quote}}
# When the config map is mounted as a volume, these will be created as files.
index.html: {{ .Values.index | quote }}
test.txt: test

@ -9,33 +9,48 @@ metadata:
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
# is responsible for.
heritage: {{ .Release.Service | quote }}
# This makes it easy to search for all components of a release using kubectl.
release: {{ .Release.Name | quote }}
heritage: {{ .Release.Service }}
# The "release" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
release: {{ .Release.Name }}
# This makes it easy to audit chart usage.
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
spec:
replicas: {{default 1 .Values.replicaCount}}
replicas: {{ .Values.replicaCount }}
template:
metadata:
{{- if .Values.podAnnotations }}
# Allows custom annotations to be specified
annotations:
{{ toYaml .Values.podAnnotations | indent 8 }}
{{- end }}
labels:
app: {{template "fullname" .}}
release: {{.Release.Name | quote }}
app: {{ template "name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: nginx
# Making image configurable is not necessary. Making imageTag configurable
# is a nice option for the user. Especially in the strange cases like
# nginx where the base distro is determined by the tag. Using :latest
# is frowned upon, using :stable isn't that great either.
image: "{{default "nginx" .Values.image}}:{{default "stable-alpine" .Values.imageTag}}"
imagePullPolicy: {{default "IfNotPresent" .Values.pullPolicy}}
- name: {{ template "name" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 80
- name: http
containerPort: 80
protocol: TCP
# This (and the volumes section below) mount the config map as a volume.
volumeMounts:
- mountPath: /usr/share/nginx/html
name: wwwdata-volume
resources:
# Allow chart users to specify resources. Usually, no default should be set, so this is left to be a conscious
# choice to the chart users and avoids that charts don't run out of the box on, e. g., Minikube when high resource
# requests are specified by default.
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector:
# Node selectors can be important on mixed Windows/Linux clusters.
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
volumes:
- name: wwwdata-volume
configMap:

@ -1,11 +1,18 @@
apiVersion: batch/v1
kind: Job
metadata:
name: "{{template "fullname" . }}"
name: {{ template "fullname" . }}
labels:
heritage: {{.Release.Service | quote }}
release: {{.Release.Name | quote }}
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
# is responsible for.
heritage: {{ .Release.Service }}
# The "release" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
release: {{ .Release.Name }}
# This makes it easy to audit chart usage.
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
annotations:
# This is what defines this resource as a hook. Without this line, the
# job is considered part of the release.
@ -13,19 +20,18 @@ metadata:
spec:
template:
metadata:
name: "{{template "fullname" . }}"
name: {{ template "fullname" . }}
labels:
heritage: {{.Release.Service | quote }}
release: {{.Release.Name | quote }}
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
release: {{ .Release.Name }}
app: {{ template "name" . }}
spec:
# This shows how to use a simple value. This will look for a passed-in value
# called restartPolicy. If it is not found, it will use the default value.
# {{ default "Never" .restartPolicy }} is a slightly optimized version of the
# more conventional syntax: {{ .restartPolicy | default "Never" }}
restartPolicy: Never
restartPolicy: {{ .Values.restartPolicy }}
containers:
- name: post-install-job
image: "alpine:3.3"
# All we're going to do is sleep for a minute, then exit.
command: ["/bin/sleep","{{default "10" .Values.sleepyTime}}"]
# All we're going to do is sleep for a while, then exit.
command: ["/bin/sleep", "{{ .Values.sleepyTime }}"]

@ -3,7 +3,12 @@
apiVersion: v1
kind: Secret
metadata:
name: "{{.Release.Name}}-secret"
name: {{ template "fullname" . }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
# This declares the resource to be a hook. By convention, we also name the
# file "pre-install-XXX.yaml", but Helm itself doesn't care about file names.
annotations:

@ -1,7 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{.Release.Name}}-service-test"
name: "{{ template "fullname" . }}-service-test"
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
annotations:
"helm.sh/hook": test-success
spec:
@ -9,5 +14,5 @@ spec:
- name: curl
image: radial/busyboxplus:curl
command: ['curl']
args: [ '{{ template "fullname" .}}:{{default 80 .Values.httpPort}}' ]
args: ['{{ template "fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

@ -0,0 +1,39 @@
apiVersion: v1
kind: Service
metadata:
{{- if .Values.service.annotations }}
annotations:
{{ toYaml .Values.service.annotations | indent 4 }}
{{- end }}
labels:
app: {{ template "name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
name: {{ template "fullname" . }}
spec:
# Provides options for the service so chart users have the full choice
type: "{{ .Values.service.type }}"
clusterIP: "{{ .Values.service.clusterIP }}"
{{- if .Values.service.externalIPs }}
externalIPs:
{{ toYaml .Values.service.externalIPs | indent 4 }}
{{- end }}
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: "{{ .Values.service.loadBalancerIP }}"
{{- end }}
{{- if .Values.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges:
{{ toYaml .Values.service.loadBalancerSourceRanges | indent 4 }}
{{- end }}
ports:
- name: http
port: {{ .Values.service.port }}
protocol: TCP
targetPort: http
{{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
selector:
app: {{ template "name" . }}
release: {{ .Release.Name }}

@ -1,18 +0,0 @@
# This is a service gateway to the replica set created by the deployment.
# Take a look at the deployment.yaml for general notes about this chart.
apiVersion: v1
kind: Service
metadata:
name: {{template "fullname" .}}
labels:
heritage: {{ .Release.Service | quote }}
release: {{ .Release.Name | quote }}
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
spec:
ports:
- port: {{default 80 .Values.httpPort}}
targetPort: 80
protocol: TCP
name: http
selector:
app: {{template "fullname" .}}

@ -2,14 +2,8 @@
# This is a YAML-formatted file.
# Declare name/value pairs to be passed into your templates.
# See the list at https://hub.docker.com/r/library/nginx/tags/
imageTag: "1.11.0"
# The port (defined on the service) to access the HTTP server.
httpPort: 8888
# Number of nginx instances to run
replicaCount: 1
restartPolicy: Never
# Evaluated by the post-install hook
sleepyTime: "10"
@ -17,3 +11,24 @@ sleepyTime: "10"
index: >-
<h1>Hello</h1>
<p>This is a test</p>
image:
repository: nginx
tag: 1.11.0
pullPolicy: IfNotPresent
service:
annotations: {}
clusterIP: ""
externalIPs: []
loadBalancerIP: ""
loadBalancerSourceRanges: []
type: ClusterIP
port: 8888
nodePort: ""
podAnnotations: {}
resources: {}
nodeSelector: {}

@ -74,14 +74,17 @@ ingress:
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
#requests:
# cpu: 100m
# memory: 128Mi
`
const defaultIgnore = `# Patterns to ignore when building packages.
@ -115,10 +118,10 @@ kind: Ingress
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
app: {{ template "name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
annotations:
{{- range $key, $value := .Values.ingress.annotations }}
{{ $key }}: {{ $value | quote }}
@ -146,13 +149,17 @@ kind: Deployment
metadata:
name: {{ template "fullname" . }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
app: {{ template "name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
template:
metadata:
labels:
app: {{ template "fullname" . }}
app: {{ template "name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
@ -169,7 +176,11 @@ spec:
path: /
port: {{ .Values.service.internalPort }}
resources:
{{ toYaml .Values.resources | indent 10 }}
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
`
const defaultService = `apiVersion: v1
@ -177,7 +188,10 @@ kind: Service
metadata:
name: {{ template "fullname" . }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
app: {{ template "name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
@ -186,7 +200,8 @@ spec:
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ template "fullname" . }}
app: {{ template "name" . }}
release: {{ .Release.Name }}
`
const defaultNotes = `1. Get the application URL by running these commands:
@ -202,7 +217,7 @@ const defaultNotes = `1. Get the application URL by running these commands:
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "fullname" . }}" -o jsonpath="{.items[0].metadata.name}")
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.service.externalPort }}
{{- end }}

@ -57,16 +57,16 @@ type Dependency struct {
// used to fetch the repository index.
Repository string `json:"repository"`
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
Condition string `json:"condition"`
Condition string `json:"condition,omitempty"`
// Tags can be used to group charts for enabling/disabling together
Tags []string `json:"tags"`
Tags []string `json:"tags,omitempty"`
// Enabled bool determines if chart should be loaded
Enabled bool `json:"enabled"`
Enabled bool `json:"enabled,omitempty"`
// ImportValues holds the mapping of source values to parent key to be imported. Each item can be a
// string or pair of child/parent sublist items.
ImportValues []interface{} `json:"import-values"`
ImportValues []interface{} `json:"import-values,omitempty"`
// Alias usable alias to be used for the chart
Alias string `json:"alias"`
Alias string `json:"alias,omitempty"`
}
// ErrNoRequirementsFile to detect error condition

@ -81,3 +81,8 @@ func (h Home) LocalRepository(elem ...string) string {
func (h Home) Plugins() string {
return h.Path("plugins")
}
// Archive returns the path to download chart archives
func (h Home) Archive() string {
return h.Path("cache", "archive")
}

@ -37,6 +37,7 @@ func TestHelmHome(t *testing.T) {
isEq(t, hh.Cache(), "/r/repository/cache")
isEq(t, hh.CacheIndex("t"), "/r/repository/cache/t-index.yaml")
isEq(t, hh.Starters(), "/r/starters")
isEq(t, hh.Archive(), "/r/cache/archive")
}
func TestHelmHome_expand(t *testing.T) {

@ -34,4 +34,5 @@ func TestHelmHome(t *testing.T) {
isEq(t, hh.Cache(), "r:\\repository\\cache")
isEq(t, hh.CacheIndex("t"), "r:\\repository\\cache\\t-index.yaml")
isEq(t, hh.Starters(), "r:\\starters")
isEq(t, hh.Archive(), "r:\\cache\\archive")
}

@ -146,7 +146,7 @@ func TestResolve(t *testing.T) {
}
func TestHashReq(t *testing.T) {
expect := "sha256:45b06fcc4496c705bf3d634f8a2ff84e6a6f0bdcaf010614b8886572d1e52b99"
expect := "sha256:e70e41f8922e19558a8bf62f591a8b70c8e4622e3c03e5415f09aba881f13885"
req := &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"},

@ -26,7 +26,7 @@ var (
// Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements.
// Increment patch number for critical fixes to existing releases.
Version = "v2.4"
Version = "v2.5"
// BuildMetadata is extra build time data
BuildMetadata = "unreleased"

Loading…
Cancel
Save