Skip to content
Commits on Source (13)
......@@ -53,6 +53,9 @@ jobs:
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
......@@ -64,3 +67,5 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
VERSION=${{ env.RELEASE_VERSION }}
......@@ -11,4 +11,5 @@ mysql.env
terrareg.env
.env
certs/*
node_modules/
\ No newline at end of file
node_modules/
terrareg/version.txt
\ No newline at end of file
image: python:3.10
image: python:3.12
# Change pip's cache directory to be inside the project directory since we can
# only cache local items.
......@@ -85,7 +85,7 @@ unit-integration-selenium-tests:
# Perform database migration
- alembic upgrade head
# Run integration tests
- http_proxy= HTTP_PROXY= coverage run -m pytest --verbose --junitxml=./pytest-report.xml ./test
- http_proxy= HTTP_PROXY= coverage run -m pytest --verbose --junitxml=./pytest-report.xml ./test || http_proxy= HTTP_PROXY= pytest --verbose --last-failed --last-failed-no-failures=none ./test
- coverage report --include='./terrareg/*'
- coverage xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
......@@ -119,7 +119,7 @@ mysql-integration-selenium-tests:
# Perform database migration
- alembic upgrade head
# Run integration tests
- http_proxy= HTTP_PROXY= coverage run -m pytest --verbose --junitxml=./pytest-report.xml ./test/integration ./test/selenium
- http_proxy= HTTP_PROXY= coverage run -m pytest --verbose --junitxml=./pytest-report.xml ./test/integration ./test/selenium || http_proxy= HTTP_PROXY= pytest --verbose --last-failed --last-failed-no-failures=none ./test/integration ./test/selenium
- coverage report --include='./terrareg/*'
- coverage xml
artifacts:
......@@ -136,9 +136,9 @@ spell-checker:
test-docker-image:
stage: test
# Use python:3.10, as it's already used
# Use python:3.12, as it's already used
# by build and comes with curl
image: python:3.10
image: python:3.12
extends: .limit_release_and_non_pushes
services:
- name: terrareg-image:v${CI_COMMIT_SHORT_SHA}
......@@ -154,9 +154,9 @@ test-docker-image:
test-docker-image-waitress:
stage: test
# Use python:3.10, as it's already used
# Use python:3.12, as it's already used
# by build and comes with curl
image: python:3.10
image: python:3.12
extends: .limit_release_and_non_pushes
services:
- name: terrareg-image:v${CI_COMMIT_SHORT_SHA}
......@@ -170,20 +170,6 @@ test-docker-image-waitress:
# Curl the terrareg homepage and ensure the title is displayed
- http_proxy= curl http://terrareg:5000 | grep 'Home - Terrareg'
build-wheel:
stage: build
extends: [.before_script_python, .limit_release_and_non_pushes]
script:
- python setup.py bdist_wheel
# an alternative approach is to install and run:
- pip install dist/*
#- pip install --proxy=$http_proxy -r requirements.txt
# run the command here
artifacts:
paths:
- dist/*.whl
build-docker-image:
stage: build
image: docker:latest
......@@ -312,7 +298,7 @@ release:
generate-docs:
stage: deploy
image: python:3.10
image: python:3.12
variables:
GITHUB_TOKEN: $GH_DEPLOY_TOKEN
script:
......
# Changelog
# [3.11.0](https://gitlab.dockstudios.co.uk/pub/terrareg/compare/v3.10.1...v3.11.0) (2024-09-12)
### Bug Fixes
* Add setuptools requirement ([9fa81e2](https://gitlab.dockstudios.co.uk/pub/terrareg/commit/9fa81e247fbb9b569dbdcb989ad49acf6fd7a1f1)), closes [#541](https://gitlab.dockstudios.co.uk/pub/terrareg/issues/541)
* Remove unused import which was causing a build error ([5edb778](https://gitlab.dockstudios.co.uk/pub/terrareg/commit/5edb7782d0b75b075dfe0183857b95c6a01ec226)), closes [#539](https://gitlab.dockstudios.co.uk/pub/terrareg/issues/539)
### Features
* Add API endpoint to provide version of Terrareg ([0ab040a](https://gitlab.dockstudios.co.uk/pub/terrareg/commit/0ab040a111da0b6d35ca589fd002fadbec238faf)), closes [#539](https://gitlab.dockstudios.co.uk/pub/terrareg/issues/539)
* Add version to footer of UI ([d76a11a](https://gitlab.dockstudios.co.uk/pub/terrareg/commit/d76a11a13561ccd8bb3bf4f66f960e175f2cb585)), closes [#539](https://gitlab.dockstudios.co.uk/pub/terrareg/issues/539)
## [3.10.1](https://gitlab.dockstudios.co.uk/pub/terrareg/compare/v3.10.0...v3.10.1) (2024-09-04)
......
FROM python:3.12-slim
ARG VERSION
WORKDIR /
RUN apt-get update && \
......@@ -92,6 +94,7 @@ COPY alembic.ini .
COPY terrareg.py .
COPY terrareg terrareg
COPY scripts scripts
RUN echo "$VERSION" > terrareg/version.txt
# Copy licenses for JS/CSS
RUN mkdir licenses/static
......
......@@ -47,4 +47,5 @@ boto3==1.34.37
botocore==1.34.37
jmespath==1.0.1
python-dateutil==2.8.2
setuptools==74.1.2
s3transfer==0.10.0
#!/usr/bin/env python
from distutils.core import setup
from setuptools import setup
with open('requirements.txt') as f:
required = f.read().splitlines()
......
from typing import Union, Tuple
from distutils.dir_util import copy_tree
from glob import glob
import json
import re
......
......@@ -670,6 +670,11 @@ class Server(BaseHandler):
'/v1/terrareg/auth/admin/is_authenticated'
)
self._api.add_resource(
ApiTerraregVersion,
'/v1/terrareg/version'
)
# Health-check endpoint
self._api.add_resource(
ApiTerraregHealth,
......
......@@ -76,6 +76,7 @@ from .provider_versions import ApiProviderVersions
from .provider_search import ApiProviderSearch
from .terrareg_provider_search_filters import ApiTerraregProviderSearchFilters
from .terrareg_provider_integrations import ApiTerraregProviderIntegrations
from .terrareg_version import ApiTerraregVersion
from .terraform.v2.gpg_keys import ApiGpgKeys
from .terraform.v2.gpg_key import ApiGpgKey
......
from terrareg.server.error_catching_resource import ErrorCatchingResource
from terrareg.version import VERSION
class ApiTerraregVersion(ErrorCatchingResource):
"""Interface to obtain version of Terrareg."""
def _get(self):
"""Return version"""
return {
"version": VERSION
}
......@@ -168,4 +168,15 @@ function convertImportedHtml(element, forceHcl=false) {
}
}
}
}
/*
* Populates version content from data from version endpoint
*/
function populateVersionText() {
$.get('/v1/terrareg/version').then((data) => {
if (data.version) {
$('#terrareg-version').text(` (${data.version})`);
}
})
}
\ No newline at end of file
......@@ -96,6 +96,7 @@
$(document).ready(() => {
setupPageLogin();
populateVersionText();
});
</script>
......@@ -237,7 +238,7 @@
<footer class="footer">
<div class="content has-text-centered">
<p>
Made with <span class="icon"><i class="far fa-heart"></i></span> - <a href="https://gitlab.dockstudios.co.uk/pub/terrareg">Terrareg</a>
Made with <span class="icon"><i class="far fa-heart"></i></span> - <a href="https://gitlab.dockstudios.co.uk/pub/terrareg">Terrareg</a><span id="terrareg-version"></span>
</p>
<p id="provider-tos">
......
from typing import Optional
import os
version_file: str = os.path.join(os.path.dirname(os.path.realpath(__file__)), "version.txt")
VERSION: Optional[str] = None
if os.path.isfile(version_file):
try:
with open(version_file, "r") as fh:
VERSION = fh.readline().strip()
except Exception as exc:
print("Failed to read version file", exc)
......@@ -197,7 +197,7 @@ class SeleniumTest(BaseTest):
if parent is None:
parent = self.selenium_instance
max_attempts = 5
max_attempts = 10
for itx in range(max_attempts):
try:
# Attempt to find element
......
......@@ -131,8 +131,7 @@ class TestAuditHistory(SeleniumTest):
user_groups_button.click()
assert self.selenium_instance.current_url == self.get_url(
'/audit-history')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/audit-history'))
@staticmethod
def _ensure_audit_row_is_like(row, audit_item):
......
......@@ -110,7 +110,7 @@ class TestModuleSearch(SeleniumTest):
self.wait_for_element(By.ID, "contributed-providersearch.mixedsearch-result.1.0.0")
button = self.selenium_instance.find_element(By.XPATH, ".//button[text()='View all provider results']")
button.click()
assert self.selenium_instance.current_url == self.get_url('/search/providers?q=mixed')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/search/providers?q=mixed'))
def test_module_results_button(self):
"""Check link to provider results"""
......@@ -119,4 +119,4 @@ class TestModuleSearch(SeleniumTest):
self.wait_for_element(By.ID, "contributed-providersearch.mixedsearch-result.1.0.0")
button = self.selenium_instance.find_element(By.XPATH, ".//button[text()='View all module results']")
button.click()
assert self.selenium_instance.current_url == self.get_url('/search/modules?q=mixed')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/search/modules?q=mixed'))
......@@ -102,7 +102,7 @@ class TestEditNamespace(SeleniumTest):
delete_button.click()
# Ensure user is still on namespace edit page
assert self.selenium_instance.current_url == self.get_url("/edit-namespace/test-deletion")
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url("/edit-namespace/test-deletion"))
# Ensure error is correctly shown
error = self.selenium_instance.find_element(By.ID, "delete-error")
......@@ -132,7 +132,7 @@ class TestEditNamespace(SeleniumTest):
delete_button.click()
# Ensure user is still on namespace edit page
assert self.selenium_instance.current_url == self.get_url("/edit-namespace/initial-providers")
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url("/edit-namespace/initial-providers"))
# Ensure error is correctly shown
error = self.selenium_instance.find_element(By.ID, "delete-error")
......
......@@ -118,7 +118,7 @@ class TestInitialSetup(SeleniumTest):
# Click link to login
login_card_content.find_element(By.TAG_NAME, 'a').click()
assert self.selenium_instance.current_url == self.get_url('/login')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/login'))
# Login as admin
self.perform_admin_authentication('admin-setup-password')
......@@ -135,7 +135,7 @@ class TestInitialSetup(SeleniumTest):
# Click link to create module
create_module_card_content.find_element(By.TAG_NAME, 'a').click()
assert self.selenium_instance.current_url == self.get_url('/create-namespace?initial_setup=1')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/create-namespace?initial_setup=1'))
# Fill out namespace form and click create
self.selenium_instance.find_element(By.ID, 'namespace-name').send_keys('unittestnamespace')
......@@ -157,7 +157,7 @@ class TestInitialSetup(SeleniumTest):
# Click link to create module
create_module_card_content.find_element(By.TAG_NAME, 'a').click()
assert self.selenium_instance.current_url == self.get_url('/create-module?initial_setup=1')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/create-module?initial_setup=1'))
# Fill out form to create module and submit
self.selenium_instance.find_element(By.ID, 'create-module-module').send_keys('setupmodulename')
......
......@@ -1778,7 +1778,7 @@ EOF
# Wait and ensure page has not changed
sleep(0.2)
assert self.selenium_instance.current_url == self.get_url('/modules/moduledetails/fullypopulated/testprovider/2.5.5#settings')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/modules/moduledetails/fullypopulated/testprovider/2.5.5#settings'))
# Ensure module version still exists
module_version._cache_db_row = None
......@@ -1792,7 +1792,7 @@ EOF
delete_button.click()
# Ensure user is redirected to module page
assert self.selenium_instance.current_url == self.get_url('/modules/moduledetails/fullypopulated/testprovider')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/modules/moduledetails/fullypopulated/testprovider'))
# Ensure module version no longer exists
assert ModuleVersion.get(module_provider=module_provider, version='2.5.5') is None
......@@ -2035,7 +2035,7 @@ EOF
# Wait and ensure page has not changed and settings tab is still displayed
sleep(0.2)
assert self.selenium_instance.current_url == self.get_url('/modules/moduledetails/fullypopulated/providertodelete#settings')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/modules/moduledetails/fullypopulated/providertodelete#settings'))
self.wait_for_element(By.ID, 'module-tab-link-settings')
# Ensure module version still exists
......@@ -2050,7 +2050,7 @@ EOF
delete_button.click()
# Ensure user is redirected to module page
assert self.selenium_instance.current_url == self.get_url('/modules/moduledetails/fullypopulated/providertodelete')
self.assert_equals(lambda: self.selenium_instance.current_url, self.get_url('/modules/moduledetails/fullypopulated/providertodelete'))
# Ensure warning about non-existent module provider is displayed
self.assert_equals(
......