Merge branch 'master' of https://github.com/inventree/InvenTree into webhook-2036

This commit is contained in:
Matthias 2021-09-14 18:17:05 +02:00
commit a344bc1ab2
No known key found for this signature in database
GPG Key ID: F50EF5741D33E076
32 changed files with 30694 additions and 27029 deletions

View File

@ -15,6 +15,9 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Check version number
run: |
python3 ci/check_version_number.py --dev
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx - name: Set up Docker Buildx

42
.github/workflows/docker_stable.yaml vendored Normal file
View File

@ -0,0 +1,42 @@
# Build and push latest docker image on push to master branch
name: Docker Build
on:
push:
branches:
- 'stable'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Check version number
run: |
python3 ci/check_version_number.py --release
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Dockerhub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v2
with:
context: ./docker
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
target: production
build-args:
branch: stable
repository: inventree/inventree
tags: inventree/inventree:stable
- name: Image Digest
run: echo ${{ steps.docker_build.outputs.digest }}

View File

@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Check Release tag - name: Check Release tag
run: | run: |
python3 ci/check_version_number.py ${{ github.event.release.tag_name }} python3 ci/check_version_number.py --release --tag ${{ github.event.release.tag_name }}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx - name: Set up Docker Buildx

20
.github/workflows/version.yaml vendored Normal file
View File

@ -0,0 +1,20 @@
# Check that the version number format matches the current branch
name: Version Numbering
on:
pull_request:
branches-ignore:
- l10*
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Check version number
run: |
python3 ci/check_version_number.py --branch ${{ github.base_ref }}

View File

@ -1,22 +1,102 @@
Contributions to InvenTree are welcomed - please follow the guidelines below. Please read the contribution guidelines below, before submitting your first pull request to the InvenTree codebase.
## Feature Branches ## Branches and Versioning
No pushing to master! New featues must be submitted in a separate branch (one branch per feature). InvenTree roughly follow the [GitLab flow](https://docs.gitlab.com/ee/topics/gitlab_flow.html) branching style, to allow simple management of multiple tagged releases, short-lived branches, and development on the main branch.
## Include Migration Files ### Version Numbering
InvenTree version numbering follows the [semantic versioning](https://semver.org/) specification.
### Master Branch
The HEAD of the "main" or "master" branch of InvenTree represents the current "latest" state of code development.
- All feature branches are merged into master
- All bug fixes are merged into master
**No pushing to master:** New featues must be submitted as a pull request from a separate branch (one branch per feature).
#### Feature Branches
Feature branches should be branched *from* the *master* branch.
- One major feature per branch / pull request
- Feature pull requests are merged back *into* the master branch
- Features *may* also be merged into a release candidate branch
### Stable Branch
The HEAD of the "stable" branch represents the latest stable release code.
- Versioned releases are merged into the "stable" branch
- Bug fix branches are made *from* the "stable" branch
#### Release Candidate Branches
- Release candidate branches are made from master, and merged into stable.
- RC branches are targetted at a major/minor version e.g. "0.5"
- When a release candidate branch is merged into *stable*, the release is tagged
#### Bugfix Branches
- If a bug is discovered in a tagged release version of InvenTree, a "bugfix" or "hotfix" branch should be made *from* that tagged release
- When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2)
- The bugfix *must* also be cherry picked into the *master* branch.
## Migration Files
Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR. Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR.
*Note: A github action checks for unstaged migration files and will reject the PR if it finds any!*
## Testing ## Unit Testing
Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage is decreased. Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage for any new features is insufficient, or the overall code coverage is decreased.
The InvenTree code base makes use of [GitHub actions](https://github.com/features/actions) to run a suite of automated tests against the code base every time a new pull request is received. These actions include (but are not limited to):
- Checking Python and Javascript code against standard style guides
- Running unit test suite
- Automated building and pushing of docker images
- Generating translation files
The various github actions can be found in the `./github/workflows` directory
## Code Style
Sumbitted Python code is automatically checked against PEP style guidelines. Locally you can run `invoke style` to ensure the style checks will pass, before submitting the PR.
## Documentation ## Documentation
New features or updates to existing features should be accompanied by user documentation. A PR with associated documentation should link to the matching PR at https://github.com/inventree/inventree-docs/ New features or updates to existing features should be accompanied by user documentation. A PR with associated documentation should link to the matching PR at https://github.com/inventree/inventree-docs/
## Code Style ## Translations
Sumbitted Python code is automatically checked against PEP style guidelines. Locally you can run `invoke style` to ensure the style checks will pass, before submitting the PR. Any user-facing strings *must* be passed through the translation engine.
- InvenTree code is written in English
- User translatable strings are provided in English as the primary language
- Secondary language translations are provided [via Crowdin](https://crowdin.com/project/inventree)
*Note: Translation files are updated via GitHub actions - you do not need to compile translations files before submitting a pull request!*
### Python Code
For strings exposed via Python code, use the following format:
```python
from django.utils.translation import ugettext_lazy as _
user_facing_string = _('This string will be exposed to the translation engine!')
```
### Templated Strings
HTML and javascript files are passed through the django templating engine. Translatable strings are implemented as follows:
```html
{% load i18n %}
<span>{% trans "This string will be translated" %} - this string will not!</span>
```

View File

@ -0,0 +1 @@
{"de": 95, "el": 0, "en": 0, "es": 4, "fr": 6, "he": 0, "id": 0, "it": 0, "ja": 4, "ko": 0, "nl": 0, "no": 0, "pl": 27, "ru": 6, "sv": 0, "th": 0, "tr": 32, "vi": 0, "zh": 1}

View File

@ -8,7 +8,7 @@ import re
import common.models import common.models
INVENTREE_SW_VERSION = "0.5.0 pre" INVENTREE_SW_VERSION = "0.5.0 dev"
INVENTREE_API_VERSION = 12 INVENTREE_API_VERSION = 12
@ -70,7 +70,7 @@ def inventreeInstanceTitle():
def inventreeVersion(): def inventreeVersion():
""" Returns the InvenTree version string """ """ Returns the InvenTree version string """
return INVENTREE_SW_VERSION return INVENTREE_SW_VERSION.lower().strip()
def inventreeVersionTuple(version=None): def inventreeVersionTuple(version=None):
@ -84,6 +84,33 @@ def inventreeVersionTuple(version=None):
return [int(g) for g in match.groups()] return [int(g) for g in match.groups()]
def isInvenTreeDevelopmentVersion():
"""
Return True if current InvenTree version is a "development" version
"""
print("is dev?", inventreeVersion())
return inventreeVersion().endswith('dev')
def inventreeDocsVersion():
"""
Return the version string matching the latest documentation.
Development -> "latest"
Release -> "major.minor"
"""
if isInvenTreeDevelopmentVersion():
return "latest"
else:
major, minor, patch = inventreeVersionTuple()
return f"{major}.{minor}"
def isInvenTreeUpToDate(): def isInvenTreeUpToDate():
""" """
Test if the InvenTree instance is "up to date" with the latest version. Test if the InvenTree instance is "up to date" with the latest version.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -667,6 +667,8 @@
}); });
onPanelLoad("test-templates", function() { onPanelLoad("test-templates", function() {
// Load test template table
loadPartTestTemplateTable( loadPartTestTemplateTable(
$("#test-template-table"), $("#test-template-table"),
{ {
@ -677,12 +679,9 @@
} }
); );
// Callback for "add test template" button
$("#add-test-template").click(function() { $("#add-test-template").click(function() {
function reloadTestTemplateTable() {
$("#test-template-table").bootstrapTable("refresh");
}
constructForm('{% url "api-part-test-template-list" %}', { constructForm('{% url "api-part-test-template-list" %}', {
method: 'POST', method: 'POST',
fields: { fields: {
@ -697,39 +696,10 @@
} }
}, },
title: '{% trans "Add Test Result Template" %}', title: '{% trans "Add Test Result Template" %}',
onSuccess: reloadTestTemplateTable onSuccess: function() {
$("#test-template-table").bootstrapTable("refresh");
}
}); });
$("#test-template-table").on('click', '.button-test-edit', function() {
var pk = $(this).attr('pk');
var url = `/api/part/test-template/${pk}/`;
constructForm(url, {
fields: {
test_name: {},
description: {},
required: {},
requires_value: {},
requires_attachment: {},
},
title: '{% trans "Edit Test Result Template" %}',
onSuccess: reloadTestTemplateTable,
});
});
$("#test-template-table").on('click', '.button-test-delete', function() {
var pk = $(this).attr('pk');
var url = `/api/part/test-template/${pk}/`;
constructForm(url, {
method: 'DELETE',
title: '{% trans "Delete Test Result Template" %}',
onSuccess: reloadTestTemplateTable,
});
});
}); });
}); });

View File

@ -136,6 +136,21 @@ def inventree_version(*args, **kwargs):
return version.inventreeVersion() return version.inventreeVersion()
@register.simple_tag()
def inventree_is_development(*args, **kwargs):
return version.isInvenTreeDevelopmentVersion()
@register.simple_tag()
def inventree_is_release(*args, **kwargs):
return not version.isInvenTreeDevelopmentVersion()
@register.simple_tag()
def inventree_docs_version(*args, **kwargs):
return version.inventreeDocsVersion()
@register.simple_tag() @register.simple_tag()
def inventree_api_version(*args, **kwargs): def inventree_api_version(*args, **kwargs):
""" Return InvenTree API version """ """ Return InvenTree API version """
@ -169,7 +184,10 @@ def inventree_github_url(*args, **kwargs):
@register.simple_tag() @register.simple_tag()
def inventree_docs_url(*args, **kwargs): def inventree_docs_url(*args, **kwargs):
""" Return URL for InvenTree documenation site """ """ Return URL for InvenTree documenation site """
return "https://inventree.readthedocs.io/"
tag = version.inventreeDocsVersion()
return f"https://inventree.readthedocs.io/en/{tag}"
@register.simple_tag() @register.simple_tag()

View File

@ -22,13 +22,39 @@
<td>{% trans "InvenTree Version" %}</td> <td>{% trans "InvenTree Version" %}</td>
<td> <td>
<a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %} <a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %}
{% inventree_is_development as dev %}
{% if dev %}
<span class='label label-blue float-right'>{% trans "Development Version" %}</span>
{% else %}
{% if up_to_date %} {% if up_to_date %}
<span class='label label-green float-right'>{% trans "Up to Date" %}</span> <span class='label label-green float-right'>{% trans "Up to Date" %}</span>
{% else %} {% else %}
<span class='label label-red float-right'>{% trans "Update Available" %}</span> <span class='label label-red float-right'>{% trans "Update Available" %}</span>
{% endif %} {% endif %}
{% endif %}
</td> </td>
</tr> </tr>
{% if dev %}
{% inventree_commit_hash as hash %}
{% if hash %}
<tr>
<td><span class='fas fa-code-branch'></span></td>
<td>{% trans "Commit Hash" %}</td><td>{{ hash }}{% include "clip.html" %}</td>
</tr>
{% endif %}
{% inventree_commit_date as commit_date %}
{% if commit_date %}
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Commit Date" %}</td><td>{{ commit_date }}{% include "clip.html" %}</td>
</tr>
{% endif %}
{% endif %}
<tr>
<td><span class='fas fa-book'></span></td>
<td>{% trans "InvenTree Documentation" %}</td>
<td><a href="{% inventree_docs_url %}">{% inventree_docs_url %}</a></td>
</tr>
<tr> <tr>
<td><span class='fas fa-code'></span></td> <td><span class='fas fa-code'></span></td>
<td>{% trans "API Version" %}</td> <td>{% trans "API Version" %}</td>
@ -44,25 +70,6 @@
<td>{% trans "Django Version" %}</td> <td>{% trans "Django Version" %}</td>
<td><a href="https://www.djangoproject.com/">{% django_version %}</a>{% include "clip.html" %}</td> <td><a href="https://www.djangoproject.com/">{% django_version %}</a>{% include "clip.html" %}</td>
</tr> </tr>
{% inventree_commit_hash as hash %}
{% if hash %}
<tr>
<td><span class='fas fa-code-branch'></span></td>
<td>{% trans "Commit Hash" %}</td><td>{{ hash }}{% include "clip.html" %}</td>
</tr>
{% endif %}
{% inventree_commit_date as commit_date %}
{% if commit_date %}
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Commit Date" %}</td><td>{{ commit_date }}{% include "clip.html" %}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-book'></span></td>
<td>{% trans "InvenTree Documentation" %}</td>
<td><a href="{% inventree_docs_url %}">{% inventree_docs_url %}</a></td>
</tr>
<tr> <tr>
<td><span class='fab fa-github'></span></td> <td><span class='fab fa-github'></span></td>
<td>{% trans "View Code on GitHub" %}</td> <td>{% trans "View Code on GitHub" %}</td>

View File

@ -793,14 +793,25 @@ function attachSecondaries(modal, secondaries) {
function insertActionButton(modal, options) { function insertActionButton(modal, options) {
/* Insert a custom submission button */ /* Insert a custom submission button */
var html = ` var element = $(modal).find('#modal-footer-buttons');
<span style='float: right;'>
<button name='${options.name}' type='submit' class='btn btn-default modal-form-button' value='${options.name}'>
${options.title}
</button>
</span>`;
$(modal).find('#modal-footer-buttons').append(html); // check if button already present
var already_present = false;
for (var child=element[0].firstElementChild; child; child=child.nextElementSibling) {
if (item.firstElementChild.name == options.name) {
already_present = true;
}
}
if (already_present == false) {
var html = `
<span style='float: right;'>
<button name='${options.name}' type='submit' class='btn btn-default modal-form-button' value='${options.name}'>
${options.title}
</button>
</span>`;
element.append(html);
}
} }
function attachButtons(modal, buttons) { function attachButtons(modal, buttons) {

View File

@ -1252,7 +1252,43 @@ function loadPartTestTemplateTable(table, options) {
} }
} }
} }
] ],
onPostBody: function() {
table.find('.button-test-edit').click(function() {
var pk = $(this).attr('pk');
var url = `/api/part/test-template/${pk}/`;
constructForm(url, {
fields: {
test_name: {},
description: {},
required: {},
requires_value: {},
requires_attachment: {},
},
title: '{% trans "Edit Test Result Template" %}',
onSuccess: function() {
table.bootstrapTable('refresh');
},
});
});
table.find('.button-test-delete').click(function() {
var pk = $(this).attr('pk');
var url = `/api/part/test-template/${pk}/`;
constructForm(url, {
method: 'DELETE',
title: '{% trans "Delete Test Result Template" %}',
onSuccess: function() {
table.bootstrapTable('refresh');
},
});
});
}
}); });
} }

View File

@ -27,12 +27,61 @@ if __name__ == '__main__':
version = results[0] version = results[0]
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('tag', help='Version tag', action='store') parser.add_argument('-t', '--tag', help='Compare against specified version tag', action='store')
parser.add_argument('-r', '--release', help='Check that this is a release version', action='store_true')
parser.add_argument('-d', '--dev', help='Check that this is a development version', action='store_true')
parser.add_argument('-b', '--branch', help='Check against a particular branch', action='store')
args = parser.parse_args() args = parser.parse_args()
if not args.tag == version: if args.branch:
print(f"Release tag '{args.tag}' does not match INVENTREE_SW_VERSION '{version}'") """
sys.exit(1) Version number requirement depends on format of branch
'master': development branch
'stable': release branch
"""
print(f"Checking version number for branch '{args.branch}'")
if args.branch == 'master':
print("- This is a development branch")
args.dev = True
elif args.branch == 'stable':
print("- This is a stable release branch")
args.release = True
if args.dev:
"""
Check that the current verrsion number matches the "development" format
e.g. "0.5 dev"
"""
pattern = "^\d+(\.\d+)+ dev$"
result = re.match(pattern, version)
if result is None:
print(f"Version number '{version}' does not match required pattern for development branch")
sys.exit(1)
elif args.release:
"""
Check that the current version number matches the "release" format
e.g. "0.5.1"
"""
pattern = "^\d+(\.\d+)+$"
result = re.match(pattern, version)
if result is None:
print(f"Version number '{version}' does not match required pattern for stable branch")
sys.exit(1)
if args.tag:
if not args.tag == version:
print(f"Release tag '{args.tag}' does not match INVENTREE_SW_VERSION '{version}'")
sys.exit(1)
sys.exit(0) sys.exit(0)