diff --git a/.github/workflows/check_charts.yaml b/.github/workflows/check_charts.yaml new file mode 100644 index 0000000..3e22595 --- /dev/null +++ b/.github/workflows/check_charts.yaml @@ -0,0 +1,77 @@ +name: Lint and Test Charts + +on: + push: + branches: + - main + - chartsimprovements + tags: + - '*' + paths: + # Only run test and docker publish if some code have changed + - 'deployment/helm/**' + - '.github/workflows/check_charts.yaml' + pull_request: + +jobs: + lint-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check Version + run: | + current_version=$(grep '^version=' pyproject.toml | cut -f2 -d= | tr -d ' ' | tr -d '"') + app_version=$(grep 'appVersion:' deployment/helm/Chart.yaml | cut -f2 -d: | tr -d ' ' | tr -d '"') + if [[ "$current_version" != "$app_version" ]]; then + echo "❌ current version from pyproject.toml ($current_version) and appVersion from Chart.yaml ($app_version) differs"; + exit 1; + fi + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: v3.9.2 + + - uses: actions/setup-python@v5 + with: + python-version: 3.7 + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.6.1 + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --chart-dirs deployment --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + echo "::set-output name=changed::true" + fi + + - name: Run chart-testing (lint) + run: ct lint --chart-dirs deployment --target-branch ${{ github.event.repository.default_branch }} + + - name: Build container + uses: docker/build-push-action@v6 + if: steps.list-changed.outputs.changed == 'true' + with: + platforms: linux/amd64 + context: . + file: webapp/Dockerfile + push: false + tags: "polder:dev" + + - name: Create kind cluster + uses: helm/kind-action@v1.10.0 + if: steps.list-changed.outputs.changed == 'true' + + - name: Load container image in kind cluster + run: kind load docker-image polder:dev --name chart-testing + if: steps.list-changed.outputs.changed == 'true' + + - name: Run chart-testing (install) + run: ct install --chart-dirs deployment/k8s + if: steps.list-changed.outputs.changed == 'true' \ No newline at end of file diff --git a/.github/workflows/webapp.yaml b/.github/workflows/webapp.yaml new file mode 100644 index 0000000..9e76909 --- /dev/null +++ b/.github/workflows/webapp.yaml @@ -0,0 +1,36 @@ +# This workflow performs basic checks: +# +# 1. run a preparation step to install and cache node modules +# 2. once prep succeeds, run lint and test in parallel +# +# The checks are skipped on the 'main' branch. The project relies on branch +# protection to avoid pushes straight to 'main'. + +name: Checks + +on: + push: + branches-ignore: + - 'main' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Build container + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64 + context: ./webapp/ + file: ./webapp/Dockerfile + push: true + tags: "polder:dev" \ No newline at end of file diff --git a/ETL/curl-get-data.sh b/ETL/curl-get-data.sh index 207f3c1..d176389 100644 --- a/ETL/curl-get-data.sh +++ b/ETL/curl-get-data.sh @@ -1,7 +1,6 @@ curl 'https://sh.dataspace.copernicus.eu/api/v1/process' \ -H 'accept: */*' \ -H 'accept-language: en-US,en;q=0.9,fr;q=0.8,it;q=0.7' \ - -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJYVUh3VWZKaHVDVWo0X3k4ZF8xM0hxWXBYMFdwdDd2anhob2FPLUxzREZFIn0.eyJleHAiOjE3MjkwNjY1NDYsImlhdCI6MTcyOTA2NTY0NiwiYXV0aF90aW1lIjoxNzI5MDY1NjQ2LCJqdGkiOiIwNjYxYjcxZC0xMGY4LTRmNDItYThlYi05ZWM0NjkzZjFmYWYiLCJpc3MiOiJodHRwczovL2lkZW50aXR5LmRhdGFzcGFjZS5jb3Blcm5pY3VzLmV1L2F1dGgvcmVhbG1zL0NEU0UiLCJhdWQiOlsiQ0xPVURGRVJST19QVUJMSUMiLCJhY2NvdW50Il0sInN1YiI6IjZlMDFiZjBiLTQ1ZGEtNDQ5MS05OTFmLWNmZDU3YmRiNGM3ZSIsInR5cCI6IkJlYXJlciIsImF6cCI6InNoLTVmOGI2MzBiLWIwODMtNDllZC1iMzQwLWI4ZjAxZWNiODFjNCIsIm5vbmNlIjoiNDIwODY3OTE4OTk1MDYyNCIsInNlc3Npb25fc3RhdGUiOiI4ODk0YjA0Mi1kNjc0LTQ5YzUtYmQ4OC03NDIzOTQ1YTUyMGYiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtY2RhcyIsImNvcGVybmljdXMtZ2VuZXJhbCJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiQVVESUVOQ0VfUFVCTElDIGVtYWlsIHByb2ZpbGUgdXNlci1jb250ZXh0Iiwic2lkIjoiODg5NGIwNDItZDY3NC00OWM1LWJkODgtNzQyMzk0NWE1MjBmIiwiZ3JvdXBfbWVtYmVyc2hpcCI6WyIvYWNjZXNzX2dyb3Vwcy91c2VyX3R5cG9sb2d5L2NvcGVybmljdXNfZ2VuZXJhbCIsIi9vcmdhbml6YXRpb25zL2RlZmF1bHQtNmUwMWJmMGItNDVkYS00NDkxLTk5MWYtY2ZkNTdiZGI0YzdlL3JlZ3VsYXJfdXNlciJdLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwib3JnYW5pemF0aW9ucyI6WyJkZWZhdWx0LTZlMDFiZjBiLTQ1ZGEtNDQ5MS05OTFmLWNmZDU3YmRiNGM3ZSJdLCJuYW1lIjoiQ0RBQiBDREFCIiwidXNlcl9jb250ZXh0X2lkIjoiMTU1NzU3ZDgtYzYxNy00NTE4LWIzMGQtZTlhNzRjZTE2NzcxIiwiY29udGV4dF9yb2xlcyI6e30sImNvbnRleHRfZ3JvdXBzIjpbIi9hY2Nlc3NfZ3JvdXBzL3VzZXJfdHlwb2xvZ3kvY29wZXJuaWN1c19nZW5lcmFsLyIsIi9vcmdhbml6YXRpb25zL2RlZmF1bHQtNmUwMWJmMGItNDVkYS00NDkxLTk5MWYtY2ZkNTdiZGI0YzdlL3JlZ3VsYXJfdXNlci8iXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiZW9kLmV4cEBnbWFpbC5jb20iLCJnaXZlbl9uYW1lIjoiQ0RBQiIsInVzZXJfY29udGV4dCI6ImRlZmF1bHQtNmUwMWJmMGItNDVkYS00NDkxLTk5MWYtY2ZkNTdiZGI0YzdlIiwiZmFtaWx5X25hbWUiOiJDREFCIiwiZW1haWwiOiJlb2QuZXhwQGdtYWlsLmNvbSJ9.GADguHmTiBY0kIEuipGH4AFHWroJim_QSUJw_Oj8SBRlUhaivGHwEHoXn0ks6iM80K_J2UXEvW-VH-XuB-Qxrd8RtZSLMzRHaAuwLeMyPrxivHl3RNE4fMbyQkVAvHMeA0p5U333ozETcZCodON2YjsAL6nz5n3fMRa41wBs3Lst9MywFm4qIHexL7MlEfiv8S45Cw7Hwz_wqG6C7RH00W21DHahIgk23xXjr_CEZtS794DnRqzFo56qB2gG6I8j1C97OmfchjmWVNwY4jxkaRbsWTtVznlicZ8O79mUjWkIbSN7Ye1YP1Q_bwHFJHnVLeNtvVFbuQqeO5XJBZJhxA' \ -H 'content-type: application/json' \ -H 'origin: https://browser.dataspace.copernicus.eu' \ -H 'priority: u=1, i' \ @@ -13,4 +12,21 @@ curl 'https://sh.dataspace.copernicus.eu/api/v1/process' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-site: same-site' \ -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \ - --data-raw '{"input":{"bounds":{"properties":{"crs":"http://www.opengis.net/def/crs/EPSG/0/3857"},"bbox":[0,5009377.085697314,5009377.085697311,10018754.171394628]},"data":[{"dataFilter":{"timeRange":{"from":"2024-10-13T00:00:00.000Z","to":"2024-10-13T23:59:59.999Z"},"mosaickingOrder":"mostRecent","previewMode":"EXTENDED_PREVIEW","maxCloudCoverage":100},"processing":{"upsampling":"NEAREST","downsampling":"NEAREST"},"type":"S5PL2"}]},"output":{"width":512,"height":512,"responses":[{"identifier":"default","format":{"type":"image/tiff"}}]},"evalscript":"//VERSION=3\nvar minVal = 0.0;\nvar maxVal = 0.0001;\nvar diff = maxVal - minVal;\nconst map = [\n\t[minVal, 0x00007f], \n\t[minVal + 0.125 * diff, 0x0000ff],\n\t[minVal + 0.375 * diff, 0x00ffff],\n\t[minVal + 0.625 * diff, 0xffff00],\n\t[minVal + 0.875 * diff, 0xff0000],\n\t[maxVal, 0x7f0000]\n]; \n\nconst visualizer = new ColorRampVisualizer(map)\nfunction setup() {\n return {\n input: [\"NO2\",\"dataMask\"],\n\toutput: [\n { id: \"default\", bands: 4 },\n { id: \"eobrowserStats\", bands: 1 },\n { id: \"dataMask\", bands: 1 },\n ],\n };\n}\n\nfunction evaluatePixel(samples) {\n const [r, g, b] = visualizer.process(samples.NO2);\n const statsVal = isFinite(samples.NO2) ? samples.NO2 : NaN;\n return {\n default: [r, g, b, samples.dataMask],\n eobrowserStats: [statsVal],\n dataMask: [samples.dataMask],\n };\n}"}' \ No newline at end of file + --data-raw '{"input":{"bounds":{"properties":{"crs":"http://www.opengis.net/def/crs/EPSG/0/3857"},"bbox":[0,5009377.085697314,5009377.085697311,10018754.171394628]},"data":[{"dataFilter":{"timeRange":{"from":"2024-10-13T00:00:00.000Z","to":"2024-10-13T23:59:59.999Z"},"mosaickingOrder":"mostRecent","previewMode":"EXTENDED_PREVIEW","maxCloudCoverage":100},"processing":{"upsampling":"NEAREST","downsampling":"NEAREST"},"type":"S5PL2"}]},"output":{"width":512,"height":512,"responses":[{"identifier":"default","format":{"type":"image/tiff"}}]},"evalscript":"//VERSION=3\nvar minVal = 0.0;\nvar maxVal = 0.0001;\nvar diff = maxVal - minVal;\nconst map = [\n\t[minVal, 0x00007f], \n\t[minVal + 0.125 * diff, 0x0000ff],\n\t[minVal + 0.375 * diff, 0x00ffff],\n\t[minVal + 0.625 * diff, 0xffff00],\n\t[minVal + 0.875 * diff, 0xff0000],\n\t[maxVal, 0x7f0000]\n]; \n\nconst visualizer = new ColorRampVisualizer(map)\nfunction setup() {\n return {\n input: [\"NO2\",\"dataMask\"],\n\toutput: [\n { id: \"default\", bands: 4 },\n { id: \"eobrowserStats\", bands: 1 },\n { id: \"dataMask\", bands: 1 },\n ],\n };\n}\n\nfunction evaluatePixel(samples) {\n const [r, g, b] = visualizer.process(samples.NO2);\n const statsVal = isFinite(samples.NO2) ? samples.NO2 : NaN;\n return {\n default: [r, g, b, samples.dataMask],\n eobrowserStats: [statsVal],\n dataMask: [samples.dataMask],\n };\n}"}' + +curl 'https://sh.dataspace.copernicus.eu/api/v1/process' \ + -H 'accept: */*' \ + -H 'accept-language: en-US,en;q=0.9,fr;q=0.8,it;q=0.7' \ + -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJYVUh3VWZKaHVDVWo0X3k4ZF8xM0hxWXBYMFdwdDd2anhob2FPLUxzREZFIn0.eyJleHAiOjE3MjkwODc4MzMsImlhdCI6MTcyOTA4NDIzMywianRpIjoiYWVmMzlmNzAtNjAzYS00ZDE2LWIzOTEtMDhhODExZWFiOTkzIiwiaXNzIjoiaHR0cHM6Ly9pZGVudGl0eS5kYXRhc3BhY2UuY29wZXJuaWN1cy5ldS9hdXRoL3JlYWxtcy9DRFNFIiwiYXVkIjoiQ0xPVURGRVJST19QVUJMSUMiLCJzdWIiOiIyMTBiNTYwYy02NzNiLTRmNTUtYmU5OC1hYjk1OTVkYzhjN2MiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJzaC0xOGJhMDM1Ny1hODY2LTRhMmQtODFmMS1iNjA4NGJlNjU4ODIiLCJzY29wZSI6IkFVRElFTkNFX1BVQkxJQyB1c2VyLWNvbnRleHQiLCJjbGllbnRIb3N0IjoiNjQuMjI1LjEyOC4xNCIsImNsaWVudElkIjoic2gtMThiYTAzNTctYTg2Ni00YTJkLTgxZjEtYjYwODRiZTY1ODgyIiwib3JnYW5pemF0aW9ucyI6WyJzaC1icm93c2VyQW5vbnltb3VzIl0sInVzZXJfY29udGV4dF9pZCI6IjYwMzYyYTc2LWQ4NmMtNGRiYy1iZDc3LTY3MmE3MmMzYTIxMyIsImNvbnRleHRfcm9sZXMiOnt9LCJjb250ZXh0X2dyb3VwcyI6WyIvb3JnYW5pemF0aW9ucy9zaC1icm93c2VyQW5vbnltb3VzLyJdLCJ1c2VyX2NvbnRleHQiOiJzaC1icm93c2VyQW5vbnltb3VzIiwiY2xpZW50QWRkcmVzcyI6IjY0LjIyNS4xMjguMTQifQ.g9dSXfqvN93PKDiKKctA2wqvIT1KxTln-hq30wZ_9La2gkACSDXDq_W4R-NReuUKja5RyfDyCfBRpv8GEw-qrmDFO893mKuW6UFP0zyzyc-_7nisFWN0chG-wmxy7mNgLZGpyt4-4Ss647XNERl-viBMnop0jPI8p8MW4FkS9PecooDUEIHoq4NBq6J30N4lcS_ZXQIV8B2HnqO2emIf7fmuAJq5jjd1bMySV_Vfk_QbSV0RTtTqEBWaDKGbWlpukga9ROqeCb99xVX37zFaLRgHJ6ASSIRsqeAxRB-bLQFXFeHhMuYpasg2CO7ZW-7UO01ohCtG2A3XfL4XNeOz_A' \ + -H 'content-type: application/json' \ + -H 'origin: https://browser.dataspace.copernicus.eu' \ + -H 'priority: u=1, i' \ + -H 'referer: https://browser.dataspace.copernicus.eu/' \ + -H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \ + -H 'sec-ch-ua-mobile: ?0' \ + -H 'sec-ch-ua-platform: "Linux"' \ + -H 'sec-fetch-dest: empty' \ + -H 'sec-fetch-mode: cors' \ + -H 'sec-fetch-site: same-site' \ + -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \ + --data-raw '{"input":{"bounds":{"properties":{"crs":"http://www.opengis.net/def/crs/EPSG/0/3857"},"bbox":[0,5009377.085697314,626172.1357121639,5635549.221409476]},"data":[{"dataFilter":{"timeRange":{"from":"2024-10-16T00:00:00.000Z","to":"2024-10-16T23:59:59.999Z"},"mosaickingOrder":"mostRecent","previewMode":"EXTENDED_PREVIEW","maxCloudCoverage":100},"processing":{"upsampling":"NEAREST","downsampling":"NEAREST"},"type":"S5PL2"}]},"output":{"width":2500,"height":2500,"responses":[{"identifier":"default","format":{"type":"image/tiff"}}]},"evalscript":"//VERSION=3\nvar minVal = 0.0;\nvar maxVal = 0.0001;\nvar diff = maxVal - minVal;\nconst map = [\n\t[minVal, 0x00007f], \n\t[minVal + 0.125 * diff, 0x0000ff],\n\t[minVal + 0.375 * diff, 0x00ffff],\n\t[minVal + 0.625 * diff, 0xffff00],\n\t[minVal + 0.875 * diff, 0xff0000],\n\t[maxVal, 0x7f0000]\n]; \n\nconst visualizer = new ColorRampVisualizer(map)\nfunction setup() {\n return {\n input: [\"NO2\",\"dataMask\"],\n\toutput: [\n { id: \"default\", bands: 4 },\n { id: \"eobrowserStats\", bands: 1 },\n { id: \"dataMask\", bands: 1 },\n ],\n };\n}\n\nfunction evaluatePixel(samples) {\n const [r, g, b] = visualizer.process(samples.NO2);\n const statsVal = isFinite(samples.NO2) ? samples.NO2 : NaN;\n return {\n default: [r, g, b, samples.dataMask],\n eobrowserStats: [statsVal],\n dataMask: [samples.dataMask],\n };\n}"}' \ No newline at end of file diff --git a/deployment/helm/Chart.lock b/deployment/helm/Chart.lock new file mode 100644 index 0000000..53c759d --- /dev/null +++ b/deployment/helm/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: eoapi + repository: https://devseed.com/eoapi-k8s/ + version: 0.4.17 +digest: sha256:adbb8a0db399bf32efbf12ffafafbf07c6d4f676f78b2bf2aa6befdc5f1a4f51 +generated: "2024-10-16T11:33:15.655622351+02:00" diff --git a/helm/Chart.yaml b/deployment/helm/Chart.yaml similarity index 100% rename from helm/Chart.yaml rename to deployment/helm/Chart.yaml diff --git a/helm/charts/eoapi-0.4.17.tgz b/deployment/helm/charts/eoapi-0.4.17.tgz similarity index 100% rename from helm/charts/eoapi-0.4.17.tgz rename to deployment/helm/charts/eoapi-0.4.17.tgz diff --git a/helm/templates/Webapp.yml b/deployment/helm/templates/Webapp.yml similarity index 100% rename from helm/templates/Webapp.yml rename to deployment/helm/templates/Webapp.yml diff --git a/helm/values.yaml b/deployment/helm/values.yaml similarity index 100% rename from helm/values.yaml rename to deployment/helm/values.yaml diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile new file mode 100644 index 0000000..f278dff --- /dev/null +++ b/dockerfiles/Dockerfile @@ -0,0 +1,6 @@ +FROM node:alpine +WORKDIR /app +COPY webapp/package.json /app +RUN yarn install +COPY webapp/. /app +CMD ["yarn", "run", "start"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..688f929 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,44 @@ +[project] +name = "polder" +description = "A simple web service to create maps and viewer using eoAPI" +readme = "README.md" +requires-python = ">=3.8" +license = {file = "LICENSE"} +authors = [ + {name = "Emmanuel Mathot", email = "emmanuel@developmentseed.com"}, + {name = "Ricardo Mestre", email = "ricardo@developmentseed.com"}, + {name = "Daniel Da Silva", email = "daniel@developmentseed.com"}, + {name = "Ciaran Sweet", email = "ciaran@developmentseed.com"}, + {name = "Olaf Veerman", email = "olaf@developmentseed.com"}, + {name = "Felix Delattre", email = "felix@developmentseed.com"}, +] +keywords = [ + "COG", + "STAC", + "MosaicJSON", + "Fastapi", + "eoAPI" +] +classifiers = [ + "Intended Audience :: Information Technology", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: GIS", +] +version="0.0.1" +dependencies = [ +] + +[project.urls] +Homepage = 'https://developmentseed.org/polder/' +Documentation = "https://developmentseed.org/polder/" +Issues = "https://github.com/developmentseed/polder/issues" +Source = "https://github.com/developmentseed/polder" +Changelog = "https://developmentseed.org/polder/release-notes/" + diff --git a/webapp/Dockerfile b/webapp/Dockerfile new file mode 100644 index 0000000..2189b0f --- /dev/null +++ b/webapp/Dockerfile @@ -0,0 +1,11 @@ +FROM node:20-alpine as build +WORKDIR /src +RUN corepack enable && corepack prepare yarn@stable --activate +COPY . . +RUN yarn install +RUN yarn build +RUN ls -la . + +FROM nginx:alpine +COPY --from=build /src/dist /usr/share/nginx/html +EXPOSE 8080 diff --git a/webapp/app/components/common/stressed-input.tsx b/webapp/app/components/common/stressed-input.tsx index 08f971f..01c0f9b 100644 --- a/webapp/app/components/common/stressed-input.tsx +++ b/webapp/app/components/common/stressed-input.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { Input, InputProps, keyframes } from '@chakra-ui/react'; +import { Input, InputProps } from '@chakra-ui/react'; +import { keyframes } from '@emotion/react'; import { StressedField,