Skip to content

Commit

Permalink
DAS-2267: Add CI config for testing and reporting. (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
flamingbear authored Dec 2, 2024
1 parent e532406 commit 17f2797
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 79 deletions.
89 changes: 89 additions & 0 deletions .github/workflows/publish_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# This workflow will build and publish Docker image to ghcr.io

# This workflow runs when changes are detected in the `main` branch, which
# include an update to the `docker/service_version.txt` file. The workflow can
# also be manually triggered by a repository maintainer.

# IF all pre-requisite tests pass, this workflow will build the docker images,
# push them to ghcr.io and publish a GitHub release.
name: Publish SMAP L2 Gridding Service

on:
push:
branches: [ main ]
paths: docker/service_version.txt
workflow_dispatch:

env:
IMAGE_NAME: ${{ github.repository }}
REGISTRY: ghcr.io

jobs:
run_service_tests:
uses: ./.github/workflows/run_service_tests.yml

run_lib_tests:
uses: ./.github/workflows/run_lib_tests.yml

mypy:
uses: ./.github/workflows/mypy.yml

build_and_publish:
needs: [run_service_tests, run_lib_tests, mypy]
runs-on: ubuntu-latest
environment: release
permissions:
# write permission is required to create a GitHub release
contents: write
id-token: write
packages: write
strategy:
fail-fast: false

steps:
- name: Checkout smap-l2-gridder repository
uses: actions/checkout@v4
with:
lfs: true

- name: Extract semantic version number
run: echo "semantic_version=$(cat docker/service_version.txt)" >> $GITHUB_ENV

- name: Extract release version notes
run: |
version_release_notes=$(./bin/extract-release-notes.sh)
echo "RELEASE_NOTES<<EOF" >> $GITHUB_ENV
echo "${version_release_notes}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Log-in to ghcr.io registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Add tags to the Docker image
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}},value=${{ env.semantic_version }}
- name: Push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: docker/service.Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Publish GitHub release
uses: ncipollo/release-action@v1
with:
body: ${{ env.RELEASE_NOTES }}
commit: main
name: Version ${{ env.semantic_version }}
tag: ${{ env.semantic_version }}
34 changes: 34 additions & 0 deletions .github/workflows/run_lib_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This workflow will run the appropriate library tests across a python matrix of versions.
name: Run Python library tests

on:
workflow_call

jobs:
build_and_test_lib:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12']

steps:
- name: Checkout smap-l2-gridder repository
uses: actions/checkout@v4
with:
lfs: true

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install -r pip_requirements.txt -r tests/pip_test_requirements.txt
- name: Run science tests while excluding the service tests.
run: |
pytest tests --ignore tests/test_service
37 changes: 37 additions & 0 deletions .github/workflows/run_service_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This workflow will build the service and test Docker images for smap-l2-gridder,
# then run the `pytest` suite within a test Docker container, reporting
# test results and code coverage as artefacts. It will be called by the
# workflow that run tests against new PRs and as a first step in the workflow
# that publishes new Docker images.

name: Run Python Service Tests

on:
workflow_call

jobs:
build_and_test_service:
runs-on: ubuntu-latest
strategy:
fail-fast: false

steps:
- name: Checkout smap-l2-gridder repository
uses: actions/checkout@v4
with:
lfs: true

- name: Build service image
run: ./bin/build-image

- name: Build test image
run: ./bin/build-test

- name: Run test image
run: ./bin/run-test

- name: Archive test results and coverage
uses: actions/upload-artifact@v4
with:
name: reports
path: reports/**/*
6 changes: 6 additions & 0 deletions .github/workflows/run_tests_on_pull_requests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ on:
workflow_dispatch:

jobs:
build_and_test_service:
uses: ./.github/workflows/run_service_tests.yml

run_lib_tests:
uses: ./.github/workflows/run_lib_tests.yml

mypy:
uses: ./.github/workflows/mypy.yml
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [v0.0.1] - 2024-11-27

### Added

- Initial codebase that transforms SPL2SMP_E granules into NetCDF4-CF grids. [#1](https://github.com/nasa/harmony-SMAP-L2-gridding-service/pull/1)
- Code and configuration to wrap gridding logic into a Harmony Service [#3](https://github.com/nasa/harmony-SMAP-L2-gridding-service/pull/3 )
- GitHub actions CI configuration [#4](https://github.com/nasa/harmony-SMAP-L2-gridding-service/pull/4 )


[v0.0.1]: https://github.com/nasa/harmony-SMAP-L2-gridding-service/releases/tag/0.0.1
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ pip install pre-commit
pre-commit install
```

## Versioning:
## Versioning

Docker service images for the `smap_l2_gridder` adhere to [semantic
version](https://semver.org/) numbers: major.minor.patch.
Expand All @@ -113,13 +113,54 @@ version](https://semver.org/) numbers: major.minor.patch.
* Minor increments: These are backwards compatible API changes.
* Patch increments: These updates do not affect the API to the service.

## CI/CD:
## CI/CD

The CI/CD for SMAP-L2-Gridding-Service is run on github actions with the workflows in the
`.github/workflows` directory:

* [TODO: complete this section when the above statement is true]
* `run_lib_tests.yml` - A reusable workflow that tests the library functions
against the supported python versions.
* `run_service_tests.yml` - A reusable workflow that builds the service and
test Docker images, then runs the Python unit test suite in an instance of
the test Docker container.
* `run_tests_on_pull_requests.yml` - Triggered for all PRs against the `main`
branch. It runs the workflow in `run_service_tests.yml` and
`run_lib_tests.yml` to ensure all tests pass for the new code.
* `publish_docker_image.yml` - Triggered either manually or for commits to the
`main` branch that contain changes to the `docker/service_version.txt` file.
* `publish_release.yml`<a name="release-workflow"></a> - workflow runs
automatically when there is a change to the `docker/service_version.txt`
file on the main branch. This workflow will:
* Run the full unit test suite, to prevent publication of broken code.
* Extract the semantic version number from `docker/service_version.txt`.
* Extract the released notes for the most recent version from `CHANGELOG.md`.
* Build and deploy a this service's docker image to `ghcr.io`.
* Publish a GitHub release under the semantic version number, with associated
git tag.


## Releasing

* [TODO: complete when implemented]
A release consists of a new Docker image for the harmony-SMAP-L2-gridding-service
published to github's container repository.

A release is made automatically when a commit to the main branch contains a
changes in the `docker/service_version.txt` file, see the [publish_release](#release-workflow) workflow in the CI/CD section above.

Before **merging** a PR that will trigger a release, ensure these two files are updated:

* `CHANGELOG.md` - Notes should be added to capture the changes to the service and a link to the current pull request should be included.
* `docker/service_version.txt` - The semantic version number should be updated to trigger the release.

The `CHANGELOG.md` file requires a specific format for a new release, as it
looks for the following string to define the newest release of the code
(starting at the top of the file).

```
## [vX.Y.Z] - YYYY-MM-DD
```

Where the markdown reference needs to be updated at the bottom of the file following the existing pattern.
```
[vX.Y.Z]: https://github.com/nasa/harmony-SMAP-L2-gridding-service/releases/tag/X.Y.Z
```
26 changes: 26 additions & 0 deletions bin/extract-release-notes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
###############################################################################
#
# A bash script to extract only the notes related to the most recent version of
# SMAP L2 Gridding Service from CHANGELOG.md
#
###############################################################################

CHANGELOG_FILE="CHANGELOG.md"

## captures versions
## >## v1.0.0
## >## [v1.0.0]
VERSION_PATTERN="^## [\[]v"

## captures url links
## [unreleased]:https://github.com/nasa/harmony-browse-image-generator/compare/1.2.0..HEAD
## [v1.2.0]: https://github.com/nasa/harmony-browse-image-generator/compare/1.1.0..1.2.0
LINK_PATTERN="^\[.*\].*\.\..*"

# Read the file and extract text between the first two occurrences of the
# VERSION_PATTERN
result=$(awk "/$VERSION_PATTERN/{c++; if(c==2) exit;} c==1" "$CHANGELOG_FILE")

# Print the result
echo "$result" | grep -v "$VERSION_PATTERN" | grep -v "$LINK_PATTERN"
2 changes: 1 addition & 1 deletion docker/service_version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.0
0.0.1
75 changes: 2 additions & 73 deletions smap_l2_gridder/crs.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,84 +38,13 @@ def col_row_to_xy(self, col: int, row: int) -> tuple[np.float64, np.float64]:
return x, y


# The authoritative value of well known text strings is from epsg.org

# The pyproj CRS created from this WKT string is the same as a CRS that has been
# round tripped through the CRS creation process. But the output value on the
# files CRS metadata may not match the authoritative value because of the
# different varieties of WKT. That said, the CRS created by pyproj is the same.
# i.e.
# pyproj.crs.CRS.from_wkt(EPSG_6933_WKT).to_wkt() != EPSG_6933_WKT
# but
# pyproj.crs.CRS.from_wkt(pyproj.crs.CRS.from_wkt(EPSG_6933_WKT).to_wkt())
# == pyproj.crs.CRS.from_wkt(EPSG_6933_WKT)

# NSIDC EASE-Grid 2.0 Global CRS definition
# from: https://epsg.org/crs/wkt/id/6933
EPSG_6933_WKT = (
'PROJCRS["WGS 84 / NSIDC EASE-Grid 2.0 Global",'
'BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble", '
'MEMBER["World Geodetic System 1984 (Transit)", ID["EPSG",1166]], '
'MEMBER["World Geodetic System 1984 (G730)", ID["EPSG",1152]], '
'MEMBER["World Geodetic System 1984 (G873)", ID["EPSG",1153]], '
'MEMBER["World Geodetic System 1984 (G1150)", ID["EPSG",1154]], '
'MEMBER["World Geodetic System 1984 (G1674)", ID["EPSG",1155]], '
'MEMBER["World Geodetic System 1984 (G1762)", ID["EPSG",1156]], '
'MEMBER["World Geodetic System 1984 (G2139)", ID["EPSG",1309]], '
'MEMBER["World Geodetic System 1984 (G2296)", ID["EPSG",1383]], '
'ELLIPSOID["WGS 84",6378137,298.257223563,'
'LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",7030]], '
'ENSEMBLEACCURACY[2],ID["EPSG",6326]],ID["EPSG",4326]],'
'CONVERSION["US NSIDC EASE-Grid 2.0 Global",'
'METHOD["Lambert Cylindrical Equal Area",ID["EPSG",9835]],'
'PARAMETER["Latitude of 1st standard parallel",30,'
'ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],'
'ID["EPSG",8823]],PARAMETER["Longitude of natural origin",0,'
'ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],'
'ID["EPSG",8802]],PARAMETER["False easting",0,'
'LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",8806]],'
'PARAMETER["False northing",0,LENGTHUNIT["metre",1,ID["EPSG",9001]],'
'ID["EPSG",8807]],ID["EPSG",6928]],CS[Cartesian,2,ID["EPSG",4499]],'
'AXIS["Easting (X)",east],AXIS["Northing (Y)",north],'
'LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",6933]]'
)
EPSG_6933_WKT = CRS.from_epsg(6933).to_wkt()

# NSIDC EASE-Grid 2.0 North CRS definition
# from: https://epsg.org/crs/wkt/id/6931
EPSG_6931_WKT = (
'PROJCRS["WGS 84 / NSIDC EASE-Grid 2.0 North",'
'BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble", '
'MEMBER["World Geodetic System 1984 (Transit)", ID["EPSG",1166]], '
'MEMBER["World Geodetic System 1984 (G730)", ID["EPSG",1152]], '
'MEMBER["World Geodetic System 1984 (G873)", ID["EPSG",1153]], '
'MEMBER["World Geodetic System 1984 (G1150)", ID["EPSG",1154]], '
'MEMBER["World Geodetic System 1984 (G1674)", ID["EPSG",1155]], '
'MEMBER["World Geodetic System 1984 (G1762)", ID["EPSG",1156]], '
'MEMBER["World Geodetic System 1984 (G2139)", ID["EPSG",1309]], '
'MEMBER["World Geodetic System 1984 (G2296)", ID["EPSG",1383]], '
'ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1,'
'ID["EPSG",9001]],ID["EPSG",7030]], ENSEMBLEACCURACY[2],'
'ID["EPSG",6326]],ID["EPSG",4326]],'
'CONVERSION["US NSIDC EASE-Grid 2.0 North",'
'METHOD["Lambert Azimuthal Equal Area",'
'ID["EPSG",9820]],PARAMETER["Latitude of natural origin",90,'
'ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8801]],'
'PARAMETER["Longitude of natural origin",0,'
'ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]],ID["EPSG",8802]],'
'PARAMETER["False easting",0,LENGTHUNIT["metre",1,ID["EPSG",9001]],'
'ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1,'
'ID["EPSG",9001]],ID["EPSG",8807]],ID["EPSG",6929]],CS[Cartesian,2,'
'ID["EPSG",4469]],AXIS["Easting (X)",South,MERIDIAN[90.0,'
'ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]]]],'
'AXIS["Northing (Y)",South,MERIDIAN[180.0,'
'ANGLEUNIT["degree",0.0174532925199433,ID["EPSG",9102]]]],'
'LENGTHUNIT["metre",1,ID["EPSG",9001]],ID["EPSG",6931]]'
)

GPD_TO_WKT = {
'EASE2_N09km.gpd': EPSG_6931_WKT,
'EASE2_M09km.gpd': EPSG_6933_WKT,
}
EPSG_6931_WKT = CRS.from_epsg(6931).to_wkt()


def geotransform_from_target_info(target_info: dict) -> Geotransform:
Expand Down

0 comments on commit 17f2797

Please sign in to comment.