Commit 25ce84fc authored by Łukasz Nowak's avatar Łukasz Nowak

Update Release Candidate

parents f01ecbb1 69e1c0c9
Pipeline #13332 failed with stage
in 0 seconds
......@@ -28,7 +28,7 @@ md5sum = 4f4d1e7de19c77157be0c2a825b31026
shared = true
patch-options = -p1
patches =
${:_profile_base_location_}/trafficserver-8.1-stale-negative-cache-not-returnable.patch#e1a2f8a23f00cee1301ccf1a84e46763
https://github.com/apache/trafficserver/commit/3d0492f7276bb93aff499f637ff843c45908b193.patch#f23dd950acc0395fe7bc72264987c013
configure-options =
--with-openssl=${openssl:location}
--with-pcre=${pcre:location}
......
--- trafficserver-8.1.1/proxy/http/HttpTransact.cc.orig 2020-12-01 00:30:26.000000000 +0100
+++ trafficserver-8.1.1/proxy/http/HttpTransact.cc 2021-01-11 11:35:41.946893735 +0100
@@ -5752,6 +5752,7 @@
HttpTransact::is_stale_cache_response_returnable(State *s)
{
HTTPHdr *cached_response = s->cache_info.object_read->response_get();
+ HTTPStatus cache_response_code = cached_response->status_get();
// First check if client allows cached response
// Note does_client_permit_lookup was set to
@@ -5760,6 +5761,12 @@
if (!s->cache_info.directives.does_client_permit_lookup) {
return false;
}
+ // We don't serve stale negative cache.
+ if (cache_response_code == HTTP_STATUS_INTERNAL_SERVER_ERROR || cache_response_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
+ cache_response_code == HTTP_STATUS_BAD_GATEWAY || cache_response_code == HTTP_STATUS_SERVICE_UNAVAILABLE) {
+ TxnDebug("http_trans", "[is_stale_cache_response_returnable] stale negative cache");
+ return false;
+ }
// Spec says that we can not serve a stale document with a
// "must-revalidate header"
// How about "s-maxage" and "no-cache" directives?
......@@ -60,14 +60,12 @@ This is to deploy an entire frontend server with a public IPv4. If you want to
First, you will need to request a "master" instance of Caddy Frontend with:
* A ``domain`` parameter where the frontend will be available
* A ``public-ipv4`` parameter to state which public IPv4 will be used
like::
<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="domain">moulefrite.org</parameter>
<parameter id="public-ipv4">xxx.xxx.xxx.xxx</parameter>
</instance>
Then, it is possible to request many slave instances (currently only from slapconsole, UI doesn't work yet) of Caddy Frontend, like::
......@@ -189,10 +187,6 @@ Name of the domain to be used (example: mydomain.com). Sub domains of this domai
Using the IP given by the Master Instance. "domain" is a mandatory Parameter.
public-ipv4
~~~~~~~~~~~
Public ipv4 of the frontend (the one Caddy will be indirectly listening to)
port
~~~~
Port used by Caddy. Optional parameter, defaults to 4443.
......
......@@ -14,7 +14,7 @@
# not need these here).
[template]
filename = instance.cfg.in
md5sum = 28bf0c4c75c028bed79fc38786831b3e
md5sum = de69a8c408ce4f228fc22eacb7e96657
[profile-common]
filename = instance-common.cfg.in
......@@ -22,15 +22,15 @@ md5sum = 5784bea3bd608913769ff9a8afcccb68
[profile-caddy-frontend]
filename = instance-apache-frontend.cfg.in
md5sum = e8db3179e3278c6390a786cdcc947173
md5sum = a6a626fd1579fd1d4b80ea67433ca16a
[profile-caddy-replicate]
filename = instance-apache-replicate.cfg.in
md5sum = 2329022227099971a57f710832509153
md5sum = 7cb8157d2b368ab3b281ea42f743eb9c
[profile-slave-list]
_update_hash_filename_ = templates/apache-custom-slave-list.cfg.in
md5sum = 2cbcdff6fe75ec469ab7d6accd72f83c
md5sum = 772c04c165fdae91299fd909e061f926
[profile-replicate-publish-slave-information]
_update_hash_filename_ = templates/replicate-publish-slave-information.cfg.in
......@@ -46,11 +46,11 @@ md5sum = 88af61e7abbf30dc99a1a2526161128d
[template-default-slave-virtualhost]
_update_hash_filename_ = templates/default-virtualhost.conf.in
md5sum = bd9e269130bac989faa639e0903814e2
md5sum = a0ae858a3db8825c22d33d323392f588
[template-backend-haproxy-configuration]
_update_hash_filename_ = templates/backend-haproxy.cfg.in
md5sum = 5c807d34198f334b143cfa9263f6bc4e
md5sum = 0923a9227c131d2f1e11d7ddd5b15673
[template-empty]
_update_hash_filename_ = templates/empty.in
......
......@@ -287,6 +287,7 @@ extra-context =
key backend_client_caucase_url :backend-client-caucase-url
import urlparse_module urlparse
import furl_module furl
import urllib_module urllib
key master_key_download_url :master_key_download_url
key autocert caddy-directory:autocert
key caddy_log_directory caddy-directory:slave-log
......
......@@ -15,13 +15,13 @@
'enable-http2-by-default',
'global-disable-http2',
'mpm-graceful-shutdown-timeout',
'public-ipv4',
're6st-verification-url',
'backend-connect-timeout',
'backend-connect-retries',
'ciphers',
'request-timeout',
'authenticate-to-backend',
'strict-transport-security',
]
%}
{% set aikc_enabled = slapparameter_dict.get('automatic-internal-kedifa-caucase-csr', 'true').lower() in TRUE_VALUES %}
......@@ -135,23 +135,30 @@ context =
{% if backend_active_check_http_version not in ['HTTP/1.1', 'HTTP/1.0'] %}
{% do slave_error_list.append('Wrong backend-active-check-http-version %s' % (backend_active_check_http_version,)) %}
{% endif %}
{% set backend_active_check_timeout = (slave.get('backend-active-check-timeout') or '2') | int(False) %}
{% if backend_active_check_timeout in [False] or backend_active_check_timeout <= 0 %}
{% set backend_active_check_timeout = (slave.get('backend-active-check-timeout') or '2') | int(false) %}
{% if backend_active_check_timeout is false or backend_active_check_timeout <= 0 %}
{% do slave_error_list.append('Wrong backend-active-check-timeout %s' % (slave.get('backend-active-check-timeout'),)) %}
{% endif %}
{% set backend_active_check_interval = (slave.get('backend-active-check-interval') or '5') | int(False) %}
{% if backend_active_check_interval in [False] or backend_active_check_interval <= 0 %}
{% set backend_active_check_interval = (slave.get('backend-active-check-interval') or '5') | int(false) %}
{% if backend_active_check_interval is false or backend_active_check_interval <= 0 %}
{% do slave_error_list.append('Wrong backend-active-check-interval %s' % (slave.get('backend-active-check-interval'),)) %}
{% endif %}
{% set backend_active_check_rise = (slave.get('backend-active-check-rise') or '1') | int(False) %}
{% if backend_active_check_rise in [False] or backend_active_check_rise <= 0 %}
{% set backend_active_check_rise = (slave.get('backend-active-check-rise') or '1') | int(false) %}
{% if backend_active_check_rise is false or backend_active_check_rise <= 0 %}
{% do slave_error_list.append('Wrong backend-active-check-rise %s' % (slave.get('backend-active-check-rise'),)) %}
{% endif %}
{% set backend_active_check_fall = (slave.get('backend-active-check-fall') or '1') | int(False) %}
{% if backend_active_check_fall in [False] or backend_active_check_fall <= 0 %}
{% set backend_active_check_fall = (slave.get('backend-active-check-fall') or '1') | int(false) %}
{% if backend_active_check_fall is false or backend_active_check_fall <= 0 %}
{% do slave_error_list.append('Wrong backend-active-check-fall %s' % (slave.get('backend-active-check-fall'),)) %}
{% endif %}
{% endif %}
{# Check virtualhostroot-http-port and virtualhostroot-https-port #}
{% for key in ['virtualhostroot-http-port', 'virtualhostroot-https-port'] %}
{% set value = (slave.get(key) or '1') | int(false) %}
{% if value is false or value < 0 %}
{% do slave_error_list.append('Wrong %s %r' % (key, slave.get(key))) %}
{% endif %}
{% endfor %}
{# Check ciphers #}
{% set slave_cipher_list = slave.get('ciphers', '').strip().split() %}
{% if slave_cipher_list %}
......@@ -161,6 +168,11 @@ context =
{% endif %}
{% endfor %}
{% endif %}
{# Check strict-transport-security #}
{% set strict_transport_security = (slave.get('strict-transport-security') or '0') | int(false) %}
{% if strict_transport_security is false or strict_transport_security < 0 %}
{% do slave_error_list.append('Wrong strict-transport-security %s' % (slave.get('strict-transport-security'),)) %}
{% endif %}
{% set custom_domain = slave.get('custom_domain') %}
{% if custom_domain and custom_domain in used_host_list %}
{% do slave_error_list.append('custom_domain %r clashes' % (custom_domain,)) %}
......@@ -264,7 +276,7 @@ config-monitor-username = ${monitor-instance-parameter:username}
config-monitor-password = ${monitor-htpasswd:passwd}
software-type = {{frontend_type}}
return = private-ipv4 public-ipv4 slave-instance-information-list monitor-base-url backend-client-csr_id-url csr_id-url csr_id-certificate backend-haproxy-statistic-url
return = slave-instance-information-list monitor-base-url backend-client-csr_id-url csr_id-url csr_id-certificate backend-haproxy-statistic-url
{#- Send only needed parameters to frontend nodes #}
{%- set base_node_configuration_dict = {} %}
......
......@@ -50,17 +50,6 @@
"title": "Duration of the graceful shutdown period. Warning: Changing the parameter will result in restarting Caddy process.",
"type": "integer"
},
"nginx-domain": {
"description": "Base Domain for create subdomains (ie.: example2.com) for websocket, notebook and eventsource.",
"pattern": "^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$",
"title": "[NOT IMPLEMENTED] Nginx Domain",
"type": "string"
},
"public-ipv4": {
"description": "Public ipv4 of the frontend (the one Caddy will be indirectly listening to).",
"title": "Public IPv4",
"type": "string"
},
"re6st-verification-url": {
"description": "Url to verify if the internet and/or re6stnet is working.",
"title": "Test Verification URL",
......@@ -118,6 +107,12 @@
],
"title": "Authenticate to backend",
"type": "string"
},
"strict-transport-security": {
"title": "Strict Transport Security",
"description": "Enables Strict Transport Security (HSTS) on the slave, the default 0 results with option disabled. More information: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security",
"default": "0",
"type": "integer"
}
},
"title": "Input Parameters",
......
......@@ -285,6 +285,32 @@
"description": "Amount of bad responses from the backend to consider it down.",
"default": "1",
"type": "integer"
},
"strict-transport-security": {
"title": "Strict Transport Security",
"description": "Enables Strict Transport Security (HSTS) on the slave, the default 0 results with option disabled. More information: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security",
"default": "0",
"type": "integer"
},
"strict-transport-security-sub-domains": {
"title": "Strict Transport Security Sub Domains",
"description": "Configures Strict Transport Security for sub domains.",
"enum": [
"false",
"true"
],
"type": "string",
"default": "false"
},
"strict-transport-security-preload": {
"title": "Strict Transport Security Preload",
"description": "Configures Strict Transport Security preload mechanism.",
"enum": [
"false",
"true"
],
"type": "string",
"default": "false"
}
},
"title": "Input Parameters",
......
......@@ -18,10 +18,6 @@
"description": "List of URLs to access logs",
"type": "array"
},
"public-ipv4": {
"description": "Public IPv4 to be included on DNS",
"type": "string"
},
"replication_number": {
"description": "Number of nodes the slave is replicated",
"type": "integer"
......
......@@ -78,7 +78,6 @@ cert = ${slap-connection:cert-file}
# All parameters are available through the configuration.XX syntax.
# All possible parameters should have a default.
configuration.domain = example.org
configuration.public-ipv4 =
configuration.port = 4443
configuration.plain_http_port = 8080
configuration.plain_nginx_port = 8081
......@@ -107,3 +106,4 @@ configuration.backend-haproxy-statistic-port = 21444
configuration.authenticate-to-backend = False
configuration.rotate-num = 4000
configuration.slave-introspection-https-port = 22443
configuration.strict-transport-security = 0
......@@ -18,12 +18,8 @@ defaults
{%- macro frontend_entry(slave_instance, scheme, wildcard) %}
{#- wildcard switch allows to put dangerous entries in the end, as haproxy parses with first match #}
{%- if slave_instance[SCHEME_PREFIX_MAPPING[scheme]]['hostname'] and slave_instance[SCHEME_PREFIX_MAPPING[scheme]]['port'] %}
{%- set host_list = (slave_instance.get('server-alias') or '').split() %}
{%- if slave_instance.get('custom_domain') not in host_list %}
{%- do host_list.append(slave_instance.get('custom_domain')) %}
{%- endif %}
{%- set matched = {'count': 0} %}
{%- for host in host_list %}
{%- for host in slave_instance['host_list'] %}
{#- Match up to the end or optional port (starting with ':') #}
{#- Please note that this matching is quite sensitive to changes and hard to test, so avoid needless changes #}
{%- if wildcard and host.startswith('*.') %}
......@@ -80,10 +76,9 @@ frontend https-backend
{%- do ssl_list.append('crt %s' % (configuration['certificate'],)) %}
{%- endif %}
{%- do ssl_list.append('ssl verify') %}
{%- set path_to_ssl_proxy_ca_crt = slave_instance.get('path_to_ssl_proxy_ca_crt') %}
{%- if slave_instance['ssl_proxy_verify'] %}
{%- if path_to_ssl_proxy_ca_crt %}
{%- do ssl_list.append('required ca-file %s' % (path_to_ssl_proxy_ca_crt,)) %}
{%- if slave_instance['path_to_ssl_proxy_ca_crt'] %}
{%- do ssl_list.append('required ca-file %s' % (slave_instance['path_to_ssl_proxy_ca_crt'],)) %}
{%- else %}
{#- Backend SSL shall be verified, but not CA provided, disallow connection #}
{#- Simply dropping hostname from the dict will result with ignoring it... #}
......
This diff is collapsed.
......@@ -145,7 +145,15 @@ class BalancerTestCase(ERP5InstanceTestCase):
# XXX what is this ? should probably not be needed here
'name': cls.__name__,
'monitor-passwd': 'secret',
'apachedex-configuration': '--erp5-base +erp5 .*/VirtualHostRoot/erp5(/|\\?|$) --base +other / --skip-user-agent Zabbix --error-detail --js-embed --quiet',
'apachedex-configuration': [
'--logformat', '%h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i" %{ms}T',
'--erp5-base', '+erp5', '.*/VirtualHostRoot/erp5(/|\\?|$)',
'--base', '+other', '/',
'--skip-user-agent', 'Zabbix',
'--error-detail',
'--js-embed',
'--quiet',
],
'apachedex-promise-threshold': 100,
'haproxy-server-check-path': '/',
'zope-family-dict': {
......
......@@ -23,7 +23,7 @@ md5sum = 9340498841caa5771f40f8c9e561eacd
[jupyter-notebook-config]
filename = jupyter_notebook_config.py.jinja
md5sum = 6e01da7d35c1d65d4a450b0f011b296d
md5sum = 089e4c511a3c7b110471bf41ca2695a4
[erp5-kernel]
filename = ERP5kernel.py
......
......@@ -10,5 +10,12 @@ c = get_config()
c.NotebookApp.password = passwd("{{ password }}")
os.environ['PATH'] = "{{ gcc_location }}/bin" + os.pathsep + os.environ['PATH']
os.environ['PYTHONPATH'] = "{{ cythonplus_repository }}" + os.pathsep + os.environ['PYTHONPATH']
try:
os.environ['PATH'] = "{{ gcc_location }}/bin" + os.pathsep + os.environ['PATH']
except KeyError:
os.environ['PATH'] = "{{ gcc_location }}/bin"
try:
os.environ['PYTHONPATH'] = "{{ cythonplus_repository }}" + os.pathsep + os.environ['PYTHONPATH']
except KeyError:
os.environ['PYTHONPATH'] = "{{ cythonplus_repository }}"
......@@ -14,11 +14,11 @@
# not need these here).
[template-erp5]
filename = instance-erp5.cfg.in
md5sum = 2ef0ddc206c6b0982a37cfc21f23e423
md5sum = 4d0839e359c98ba3cd516903b72f798b
[template-balancer]
filename = instance-balancer.cfg.in
md5sum = 4998e62351f54700ee23a2ca8cd89329
md5sum = 9c67c77eab5195b2674e340fb44c48a2
[template-apache-backend-conf]
filename = apache-backend.conf.in
......
......@@ -259,14 +259,24 @@ command = ${monitor-generate-apachedex-report-wrapper:wrapper-path}
[monitor-generate-apachedex-report-wrapper]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:bin}/${:command}
command-line = "{{ parameter_dict['run-apachedex-location'] }}" "{{ parameter_dict['apachedex-location'] }}" "${directory:apachedex}" ${monitor-publish-parameters:monitor-base-url}/private/apachedex --apache-log-list "${apachedex-parameters:apache-log-list}" --configuration "${apachedex-parameters:configuration}"
command-line = "{{ parameter_dict['run-apachedex-location'] }}" "{{ parameter_dict['apachedex-location'] }}" "${directory:apachedex}" ${monitor-publish-parameters:monitor-base-url}/private/apachedex --apache-log-list "${apachedex-parameters:apache-log-list}" --configuration ${apachedex-parameters:configuration}
command = generate-apachedex-report
[monitor-apachedex-report-config]
recipe = slapos.recipe.template:jinja2
rendered = ${directory:etc}/${:_buildout_section_name_}
template = inline:
{% for line in slapparameter_dict['apachedex-configuration'] %}
{# apachedex config files use shlex.split, so we need to quote the arguments. #}
{# BBB: in python 3 we can use shlex.quote instead. #}
{{ repr(line.encode('utf-8')) }}
{% endfor %}
[apachedex-parameters]
# XXX - Sample log file with curent date: apache_access.log-%(date)s.gz
# which will be equivalent to apache_access.log-20150112.gz if the date is 2015-01-12
apache-log-list = ${apache-conf-parameter-dict:access-log}
configuration = {{ slapparameter_dict['apachedex-configuration'] }}
configuration = ${monitor-apachedex-report-config:rendered}
promise-threshold = {{ slapparameter_dict['apachedex-promise-threshold'] }}
[{{ section('monitor-promise-apachedex-result') }}]
......
......@@ -374,8 +374,17 @@ config-shared-certificate-authority-path = ${directory:ca-dir}
config-backend-path-dict = {{ dumps(zope_backend_path_dict) }}
config-ssl-authentication-dict = {{ dumps(ssl_authentication_dict) }}
config-apachedex-promise-threshold = {{ dumps(monitor_dict.get('apachedex-promise-threshold', 70)) }}
config-apachedex-configuration = {{ dumps(monitor_dict.get('apachedex-configuration',
'--erp5-base +erp5 .*/VirtualHostRoot/erp5(/|\\?|$) --base +other / --skip-user-agent Zabbix --error-detail --js-embed --quiet')) }}
config-apachedex-configuration = {{
dumps(
monitor_dict.get(
'apachedex-configuration',
[
'--erp5-base', '+erp5', '.*/VirtualHostRoot/erp5(/|\\?|$)',
'--base', '+other', '/',
'--skip-user-agent', 'Zabbix',
'--error-detail',
'--js-embed',
'--quiet'])) }}
[request-frontend-base]
{% if has_frontend -%}
......
......@@ -623,7 +623,7 @@ zope.app.publication = 3.14.0
zope.app.testing = 3.8.1
# Pinned versions
APacheDEX = 1.6.2
APacheDEX = 1.8
Pillow = 6.2.2
Products.CMFActionIcons = 2.1.3
Products.DCWorkflowGraph = 0.4.1
......
......@@ -74,7 +74,7 @@ md5sum = b5ac16fdeed8863e465e955ba6d1e12a
[template-erp5]
filename = instance-erp5.cfg.in
md5sum = edfd681e3a477855a581f8d01c717a92
md5sum = 548d99118afa736e5a7c428b0c8ed560
[template-zeo]
filename = instance-zeo.cfg.in
......@@ -86,7 +86,7 @@ md5sum = c03f93f95333e6a61b857dcfab7f9c0e
[template-balancer]
filename = instance-balancer.cfg.in
md5sum = 4a119083eab1eadbaf44468eb4f3381f
md5sum = 8ad9137310ae0403d433bb3c0d93be9f
[template-haproxy-cfg]
filename = haproxy.cfg.in
......
......@@ -360,12 +360,22 @@ command = ${monitor-generate-apachedex-report-wrapper:wrapper-path}
[monitor-generate-apachedex-report-wrapper]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:bin}/${:command}
command-line = "{{ parameter_dict['run-apachedex-location'] }}" "{{ parameter_dict['apachedex-location'] }}" "${directory:apachedex}" ${monitor-publish-parameters:monitor-base-url}/private/apachedex --apache-log-list "${apachedex-parameters:apache-log-list}" --configuration "${apachedex-parameters:configuration}"
command-line = "{{ parameter_dict['run-apachedex-location'] }}" "{{ parameter_dict['apachedex-location'] }}" "${directory:apachedex}" ${monitor-publish-parameters:monitor-base-url}/private/apachedex --apache-log-list "${apachedex-parameters:apache-log-list}" --configuration ${apachedex-parameters:configuration}
command = generate-apachedex-report
[monitor-apachedex-report-config]
recipe = slapos.recipe.template:jinja2
rendered = ${directory:etc}/${:_buildout_section_name_}
template = inline:
{% for line in slapparameter_dict['apachedex-configuration'] %}
{# apachedex config files use shlex.split, so we need to quote the arguments. #}
{# BBB: in python 3 we can use shlex.quote instead. #}
{{ repr(line.encode('utf-8')) }}
{% endfor %}
[apachedex-parameters]
apache-log-list = ${rsyslogd-cfg-parameter-dict:access-log-file}
configuration = {{ slapparameter_dict['apachedex-configuration'] }}
configuration = ${monitor-apachedex-report-config:rendered}
promise-threshold = {{ slapparameter_dict['apachedex-promise-threshold'] }}
[{{ section('monitor-promise-apachedex-result') }}]
......
......@@ -350,8 +350,18 @@ config-name = ${:name}
config-backend-path-dict = {{ dumps(zope_backend_path_dict) }}
config-ssl-authentication-dict = {{ dumps(ssl_authentication_dict) }}
config-apachedex-promise-threshold = {{ dumps(monitor_dict.get('apachedex-promise-threshold', 70)) }}
config-apachedex-configuration = {{ dumps(monitor_dict.get('apachedex-configuration',
'--erp5-base +erp5 .*/VirtualHostRoot/erp5(/|\\?|$) --base +other / --skip-user-agent Zabbix --error-detail --js-embed --quiet')) }}
config-apachedex-configuration = {{
dumps(
monitor_dict.get(
'apachedex-configuration',
[
'--logformat', '%h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i" %{ms}T',
'--erp5-base', '+erp5', '.*/VirtualHostRoot/erp5(/|\\?|$)',
'--base', '+other', '/',
'--skip-user-agent', 'Zabbix',
'--error-detail',
'--js-embed',
'--quiet'])) }}
[request-frontend-base]
{% if has_frontend -%}
......
......@@ -142,7 +142,7 @@ zc.recipe.egg = 2.0.3+slapos003
# Use own version of h.r.download to be able to open .xz and .lz archives
hexagonit.recipe.download = 1.7.post4
Jinja2 = 2.9.5
Jinja2 = 2.11.2
Importing = 1.10
MarkupSafe = 1.0
PyYAML = 3.13
......@@ -200,7 +200,7 @@ slapos.rebootstrap = 4.5
slapos.recipe.build = 0.46
slapos.recipe.cmmi = 0.16
slapos.recipe.template = 4.5
slapos.toolbox = 0.112
slapos.toolbox = 0.115
stevedore = 1.21.0
subprocess32 = 3.5.3
unicodecsv = 0.14.1
......
#!/bin/bash
set -e
# Go to master
git checkout master
# Clean up 1.0
git branch -D 1.0 || echo
# Checkout 1.0
git checkout 1.0
git checkout origin/1.0 -b 1.0
# Reset and Clean
git reset --hard origin/1.0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment