diff --git a/.github/workflows/component-container-image-security.yml b/.github/workflows/component-container-image-security.yml new file mode 100644 index 0000000..5c5f5ce --- /dev/null +++ b/.github/workflows/component-container-image-security.yml @@ -0,0 +1,64 @@ +name: Generic security scan + +on: + workflow_call: + inputs: + context: + description: "Path to the Dockerfile directory to analyse" + required: true + default: "." + type: string + image-path: + description: "Path of the docker image to analyse" + default: "ghcr.io/${{ github.repository }}/app" + type: string + +permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + +jobs: + security-dependency-trivy: + name: Trivy dependency scan + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + # need to format as github.repository contains uppercase + # and pull request workflow contains slashes + - id: format + name: Format proper image path and tag + env: + IMAGE_PATH: ${{ inputs.image-path }} + run: | + echo "image-tag=${GITHUB_REF_NAME/\//-}" >> $GITHUB_OUTPUT + echo "image-path=${IMAGE_PATH@L}" >> $GITHUB_OUTPUT + - name: Build Docker image + uses: docker/build-push-action@v6 + if: ${{ github.event_name != 'release' }} + with: + context: "{{defaultContext}}:${{ inputs.context }}" + tags: "${{ steps.format.outputs.image-path }}:${{ steps.format.outputs.image-tag }}" + push: false + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.28.0 + env: + TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db,aquasec/trivy-db,ghcr.io/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db,aquasec/trivy-java-db,ghcr.io/aquasecurity/trivy-java-db + with: + image-ref: "${{ steps.format.outputs.image-path }}:${{ steps.format.outputs.image-tag }}" + format: 'sarif' + output: 'trivy-results.sarif' + vuln-type: "os,library" + severity: "CRITICAL,HIGH" + exit-code: "1" + ignore-unfixed: true + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' + - name: Inspect bandit SARIF report + if: always() + run: cat trivy-results.sarif diff --git a/.github/workflows/java-docker.yml b/.github/workflows/component-container-image.yml similarity index 50% rename from .github/workflows/java-docker.yml rename to .github/workflows/component-container-image.yml index fe94825..9d61790 100644 --- a/.github/workflows/java-docker.yml +++ b/.github/workflows/component-container-image.yml @@ -1,25 +1,31 @@ -name: Java Docker +name: Build and push Docker image on: - push: - branches: - - main - paths: - - "java-demo/**" - pull_request: - paths: - - "java-demo/**" - release: - types: [created] + workflow_call: + inputs: + context: + description: "Path to the Dockerfile directory to build" + default: "." + type: string + image-path: + description: "Path of the docker image Tag" + default: "ghcr.io/${{ github.repository }}/app" + type: string jobs: container-image-build: + name: Docker runs-on: ubuntu-latest steps: - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - id: format + name: Lowercase repository path + uses: ASzc/change-string-case-action@v1 + with: + string: ${{ inputs.image-path }} - name: Login to GitHub Container Registry uses: docker/login-action@v3 if: ${{ github.event_name == 'release' }} @@ -31,12 +37,13 @@ jobs: uses: docker/build-push-action@v6 if: ${{ github.event_name != 'release' }} with: - context: "{{defaultContext}}:java-demo" + context: "{{defaultContext}}:${{ inputs.context }}" + tags: "${{ steps.format.outputs.lowercase }}:develop" push: false - name: Build and push uses: docker/build-push-action@v6 if: ${{ github.event_name == 'release' }} with: - context: "{{defaultContext}}:java-demo" + context: "{{defaultContext}}:${{ inputs.context }}" + tags: "${{ steps.format.outputs.lowercase }}:${{ github.ref_name }}" push: true - tags: "ghcr.io/british-oceanographic-data-centre/amrit-repos/java/app:${{ github.ref_name }}" diff --git a/.github/workflows/component-java-lint.yml b/.github/workflows/component-java-lint.yml new file mode 100644 index 0000000..b6566ef --- /dev/null +++ b/.github/workflows/component-java-lint.yml @@ -0,0 +1,27 @@ +name: Java Lint + +on: + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string + java-version: + description: "Java version" + default: "21" + type: string + +jobs: + java-linter-spotless: + name: Lint with spotless + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: ${{ inputs.java-version }} + - name: Run Spotless lint + working-directory: ./${{ inputs.context }} + run: ./mvnw spotless:check diff --git a/.github/workflows/component-java-test.yml b/.github/workflows/component-java-test.yml new file mode 100644 index 0000000..dfd24bd --- /dev/null +++ b/.github/workflows/component-java-test.yml @@ -0,0 +1,32 @@ +name: Java Tests + +on: + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string + java-version: + description: "Java version" + default: "21" + type: string + +jobs: + java-test-maven: + name: Test with Maven + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: ${{ inputs.java-version }} + - name: Run Maven run tests + working-directory: ./${{ inputs.context }} + run: ./mvnw clean test + - name: Publish Test Report + uses: mikepenz/action-junit-report@v5 + if: always() + with: + report_paths: '**/target/**/TEST-*.xml' \ No newline at end of file diff --git a/.github/workflows/python-tox.yaml b/.github/workflows/component-python-lint.yml similarity index 60% rename from .github/workflows/python-tox.yaml rename to .github/workflows/component-python-lint.yml index fffd802..0b66ce8 100644 --- a/.github/workflows/python-tox.yaml +++ b/.github/workflows/component-python-lint.yml @@ -1,16 +1,13 @@ -name: Python Tox +name: Python Linting on: - push: - branches: - # Run on our main branch - - main - paths: - - example-python/** - pull_request: - # Run for any pull requests - paths: - - example-python/** + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string + jobs: tox: runs-on: ubuntu-latest @@ -20,13 +17,13 @@ jobs: python-version: ["3.10", "3.11", "3.12", "3.13"] tox-job: ["test", "build", "lint", "type"] steps: - - uses: actions/checkout@v4 # Checkout the current branch/merge state - - name: Set up Python ${{ matrix.python-version }} # Get Python ready to use + - uses: actions/checkout@v4 # Checkout the current branch/merge state + - name: Set up Python ${{ matrix.python-version }} # Get Python ready to use uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies # Get Tox and Poetry ready - working-directory: ./example-python + - name: Install dependencies # Get Tox and Poetry ready + working-directory: ./${{ inputs.context }} run: | python -m pip install --upgrade pip python -m pip install tox tox-gh-actions @@ -34,7 +31,7 @@ jobs: tox depends --recreate # Run Tox jobs - name: Tox (${{ matrix.tox-job }}) - working-directory: ./example-python + working-directory: ./${{ inputs.context }} run: | poetry config virtualenvs.create false poetry install --no-root --with ${{ matrix.tox-job }} diff --git a/.github/workflows/python-security.yml b/.github/workflows/component-python-security.yml similarity index 70% rename from .github/workflows/python-security.yml rename to .github/workflows/component-python-security.yml index 04daa9e..0cbac6f 100644 --- a/.github/workflows/python-security.yml +++ b/.github/workflows/component-python-security.yml @@ -2,30 +2,30 @@ name: Python Security on: - push: - branches: - # Run on our main branch - - main - paths: - - example-python/** - pull_request: - # Run for any pull requests - paths: - - example-python/** + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string + +permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + jobs: # https://github.com/marketplace/actions/anchore-container-scan dependency-check: + name: Grype & Pip Audit dependency scan runs-on: ubuntu-latest - permissions: - # Required for uploading sarif file - security-events: write steps: - uses: actions/checkout@v4 # Checkout the current branch/merge state - name: Grype Scan uses: anchore/scan-action@v3 id: grype-scan with: - path: example-python + path: ${{ inputs.context }} - name: Upload grype sarif file if: always() uses: github/codeql-action/upload-sarif@v3 @@ -38,8 +38,9 @@ jobs: - name: Pip Audit uses: pypa/gh-action-pip-audit@v1.1.0 with: - inputs: example-python + inputs: ${{ inputs.context }} code-check: + name: Bandit scan runs-on: ubuntu-latest strategy: matrix: @@ -54,12 +55,12 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install dependencies # Install bandit - working-directory: ./example-python + working-directory: ./${{ inputs.context }} run: | python -m pip install --upgrade pip python -m pip install bandit[toml,sarif] - name: Run Bandit - working-directory: ./example-python + working-directory: ./${{ inputs.context }} # Run bandit and output to sarif file run: | bandit -r . -c pyproject.toml -f sarif -o bandit.sarif @@ -67,8 +68,8 @@ jobs: if: always() uses: github/codeql-action/upload-sarif@v3 with: - sarif_file: example-python/bandit.sarif + sarif_file: ${{ inputs.context }}/bandit.sarif category: bandit-python-analysis - name: Inspect bandit SARIF report if: always() - run: cat example-python/bandit.sarif + run: cat ${{ inputs.context }}/bandit.sarif diff --git a/.github/workflows/component-ts-lint.yml b/.github/workflows/component-ts-lint.yml new file mode 100644 index 0000000..bc113b9 --- /dev/null +++ b/.github/workflows/component-ts-lint.yml @@ -0,0 +1,21 @@ +name: Typescript Lint + +on: + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string + +jobs: + eslint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install modules + working-directory: ./${{ inputs.context }} + run: npm install + - name: Run ESLint + working-directory: ./${{ inputs.context }} + run: npm run lint diff --git a/.github/workflows/bearer.yml b/.github/workflows/component-ts-security.yml similarity index 80% rename from .github/workflows/bearer.yml rename to .github/workflows/component-ts-security.yml index e587eee..22de4b7 100644 --- a/.github/workflows/bearer.yml +++ b/.github/workflows/component-ts-security.yml @@ -5,13 +5,15 @@ # # This workflow file requires a free account on Bearer.com to manage findings, notifications and more. # See https://docs.bearer.com/guides/bearer-cloud/ -name: Bearer +name: Typescript security on: - pull_request: - - schedule: - - cron: '*/15 * * * *' + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string permissions: contents: read # for actions/checkout to fetch code @@ -20,6 +22,7 @@ permissions: jobs: bearer: + name: Bearer scan runs-on: ubuntu-latest steps: # Checkout project source @@ -29,4 +32,4 @@ jobs: id: report uses: bearer/bearer-action@828eeb928ce2f4a7ca5ed57fb8b59508cb8c79bc with: - path: typescript-demo + path: ${{ inputs.context }} diff --git a/.github/workflows/ts-test.yml b/.github/workflows/component-ts-test.yml similarity index 52% rename from .github/workflows/ts-test.yml rename to .github/workflows/component-ts-test.yml index 7011a5b..d4f61f4 100644 --- a/.github/workflows/ts-test.yml +++ b/.github/workflows/component-ts-test.yml @@ -1,12 +1,12 @@ name: Typescript Test on: - push: - paths: - - typescript-demo/** - pull_request: - paths: - - 'typescript-demo/**' + workflow_call: + inputs: + context: + description: "Path to the module to lint" + default: "" + type: string jobs: test: @@ -16,57 +16,40 @@ jobs: # Checkout the repository - name: Checkout code uses: actions/checkout@v4 - # Set up Node.js environment - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: "22" - - # Install dependencies in the `typescript-demo` folder + # Install dependencies in the context folder - name: Install dependencies run: npm install - working-directory: ./typescript-demo - + working-directory: ./${{ inputs.context }} # Build the application - name: Build the application run: npm run build - working-directory: ./typescript-demo - + working-directory: ./${{ inputs.context }} # Start the application - name: Start the application run: npm start & - working-directory: ./typescript-demo + working-directory: ./${{ inputs.context }} env: NODE_ENV: test - - name: Run Jest tests run: npx jest --ci - working-directory: ./typescript-demo + working-directory: ./${{ inputs.context }} env: - JEST_JUNIT_OUTPUT_DIR: test-results JEST_JUNIT_OUTPUT_NAME: junit.xml - - - name: Upload test results - uses: actions/upload-artifact@v4 - with: - name: jest-test-results - path: ./typescript-demo/test-results/junit.xml - - - name: Display test results in summary - uses: dorny/test-reporter@v1 + - name: Publish Test Report + uses: mikepenz/action-junit-report@v5 + if: always() with: - name: Jest Tests - path: ./typescript-demo/test-results/junit.xml - reporter: jest-junit - fail-on-error: 'true' - use-actions-summary: 'true' - + report_paths: '**/junit.xml' # Run Cypress tests - name: Run Cypress tests uses: cypress-io/github-action@v4 with: - working-directory: ./typescript-demo + working-directory: ./${{ inputs.context }} wait-on: http://localhost:3000 wait-on-timeout: 60 spec: cypress/e2e/**/*.cy.js diff --git a/.github/workflows/java-lint.yml b/.github/workflows/java-lint.yml deleted file mode 100644 index 4c0bd1c..0000000 --- a/.github/workflows/java-lint.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Java Lint - -on: - push: - branches: - - main - paths: - - "java-demo/**" - pull_request: - paths: - - "java-demo/**" -jobs: - java-linter-spotless: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: "temurin" - java-version: "21" - - name: Run Spotless lint - working-directory: ./java-demo - run: ./mvnw spotless:check diff --git a/.github/workflows/java-security.yml b/.github/workflows/java-security.yml deleted file mode 100644 index 6a8679c..0000000 --- a/.github/workflows/java-security.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Java Security - -on: - push: - branches: - - main - paths: - - 'java-demo/**' - pull_request: - paths: - - 'java-demo/**' - -jobs: - java-security-trivy: - name: Build - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Build Docker image - uses: docker/build-push-action@v6 - with: - context: "{{defaultContext}}:java-demo" - push: false - tags: "security-check:${{ github.ref_name }}" - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@0.28.0 - env: - TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db,aquasec/trivy-db,ghcr.io/aquasecurity/trivy-db - TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db,aquasec/trivy-java-db,ghcr.io/aquasecurity/trivy-java-db - with: - image-ref: 'security-check:${{ github.ref_name }}' - format: 'table' - exit-code: '1' - ignore-unfixed: true - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' \ No newline at end of file diff --git a/.github/workflows/java-test.yml b/.github/workflows/java-test.yml deleted file mode 100644 index 03f60df..0000000 --- a/.github/workflows/java-test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Java Tests - -on: - push: - branches: - - main - paths: - - "java-demo/**" - pull_request: - paths: - - "java-demo/**" -jobs: - java-test-junit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: "temurin" - java-version: "21" - - name: Run Maven run tests - working-directory: ./java-demo - run: ./mvnw clean test diff --git a/.github/workflows/python-docker.yaml b/.github/workflows/python-docker.yaml deleted file mode 100644 index c1dca26..0000000 --- a/.github/workflows/python-docker.yaml +++ /dev/null @@ -1,44 +0,0 @@ -name: Python Docker - -on: - push: - branches: - # Run on our main branch - - main - paths: - - example-python/** - pull_request: - # Run for any pull requests - paths: - - example-python/** - release: - types: [created] - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - if: ${{ github.event_name == 'release' }} - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build - uses: docker/build-push-action@v6 - if: ${{ github.event_name != 'release' }} - with: - context: "{{defaultContext}}:example-python" - push: false - - name: Build and push - uses: docker/build-push-action@v6 - if: ${{ github.event_name == 'release' }} - with: - context: "{{defaultContext}}:example-python" - push: true - tags: "ghcr.io/british-oceanographic-data-centre/amrit-repos/python/app:${{ github.ref_name }}" diff --git a/.github/workflows/ts-build.yml b/.github/workflows/ts-build.yml deleted file mode 100644 index b33fd48..0000000 --- a/.github/workflows/ts-build.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Typescript Build - -on: - push: - branches: - - main - paths: - - typescript-demo/** - pull_request: - paths: - - 'typescript-demo/**' - release: - types: [created] - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - if: ${{ github.event_name == 'release' }} - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build - uses: docker/build-push-action@v6 - if: ${{ github.event_name != 'release' }} - with: - context: "{{defaultContext}}:typescript-demo" - push: false - - name: Build and push - uses: docker/build-push-action@v6 - if: ${{ github.event_name == 'release' }} - with: - context: "{{defaultContext}}:typescript-demo" - push: true - tags: "ghcr.io/british-oceanographic-data-centre/amrit-repos/typescript/app:${{ github.ref_name }}" diff --git a/.github/workflows/ts-lint.yml b/.github/workflows/ts-lint.yml deleted file mode 100644 index c4295d8..0000000 --- a/.github/workflows/ts-lint.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Typescript Lint - -on: - push: - branches: - # Run on our main branch - - main - paths: - - typescript-demo/** - pull_request: - # Run for any pull requests - paths: - - typescript-demo/** - -jobs: - eslint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install modules - working-directory: ./typescript-demo - run: npm install - - name: Run ESLint - working-directory: ./typescript-demo - run: npm run lint diff --git a/.github/workflows/workflow-java.yml b/.github/workflows/workflow-java.yml new file mode 100644 index 0000000..fd3f5d0 --- /dev/null +++ b/.github/workflows/workflow-java.yml @@ -0,0 +1,37 @@ +name: Java Workflow + +on: + push: + branches: + # Run on our main branch + - main + paths: + - java-demo/** + pull_request: + # Run for any pull requests + paths: + - java-demo/** + release: + types: [created] + +jobs: + java-linting: + uses: ./.github/workflows/component-java-lint.yml + with: + context: java-demo + java-testing: + uses: ./.github/workflows/component-java-test.yml + with: + context: java-demo + container-image-build: + needs: [java-linting, java-testing] + uses: ./.github/workflows/component-container-image.yml + with: + context: java-demo + image-path: ghcr.io/british-oceanographic-data-centre/amrit-repos/java/app + container-image-security: + needs: [container-image-build] + uses: ./.github/workflows/component-container-image-security.yml + with: + context: java-demo + image-path: ghcr.io/british-oceanographic-data-centre/amrit-repos/java/app diff --git a/.github/workflows/workflow-python.yml b/.github/workflows/workflow-python.yml new file mode 100644 index 0000000..990b46f --- /dev/null +++ b/.github/workflows/workflow-python.yml @@ -0,0 +1,37 @@ +name: Python Workflow + +on: + push: + branches: + # Run on our main branch + - main + paths: + - example-python/** + pull_request: + # Run for any pull requests + paths: + - example-python/** + release: + types: [created] + +jobs: + python-linting: + uses: ./.github/workflows/component-python-lint.yml + with: + context: example-python + python-security: + uses: ./.github/workflows/component-python-security.yml + with: + context: example-python + container-image-build: + needs: [python-linting, python-security] + uses: ./.github/workflows/component-container-image.yml + with: + context: example-python + image-path: ghcr.io/british-oceanographic-data-centre/amrit-repos/python/app + container-image-security: + needs: [container-image-build] + uses: ./.github/workflows/component-container-image-security.yml + with: + context: example-python + image-path: ghcr.io/british-oceanographic-data-centre/amrit-repos/python/app diff --git a/.github/workflows/workflow-ts.yml b/.github/workflows/workflow-ts.yml new file mode 100644 index 0000000..e7c5bc3 --- /dev/null +++ b/.github/workflows/workflow-ts.yml @@ -0,0 +1,41 @@ +name: TypeScript Workflow + +on: + push: + branches: + # Run on our main branch + - main + paths: + - typescript-demo/** + pull_request: + # Run for any pull requests + paths: + - typescript-demo/** + release: + types: [created] + +jobs: + ts-linting: + uses: ./.github/workflows/component-ts-lint.yml + with: + context: typescript-demo + ts-test: + uses: ./.github/workflows/component-ts-test.yml + with: + context: typescript-demo + ts-security: + uses: ./.github/workflows/component-ts-security.yml + with: + context: typescript-demo + container-image-build: + needs: [ts-linting, ts-test, ts-security] + uses: ./.github/workflows/component-container-image.yml + with: + context: typescript-demo + image-path: ghcr.io/british-oceanographic-data-centre/amrit-repos/typescript/app + container-image-security: + needs: [container-image-build] + uses: ./.github/workflows/component-container-image-security.yml + with: + context: typescript-demo + image-path: ghcr.io/british-oceanographic-data-centre/amrit-repos/typescript/app diff --git a/README.md b/README.md index b123fd1..7b682fe 100644 --- a/README.md +++ b/README.md @@ -3,69 +3,93 @@ A series of "best practice" examples for code quality, testing, and style for open source code written under the AMRIT banner, leveraging CI/CD pipelines for package publication and automatic enforcement of these requirements. ## Languages + - Java - Python - TypeScript ## Requirements + - Code linting - Code testing - Code type-checking (Python-only) - Containerised development environments and application images ## Implementation and Frameworks + | Language | Linting | Testing | Typing | Security Scanning | Containerisation | Images | CI/CD | | :------: | :-----: | :-----: | :----: | :---------------: | :--------------: | :----: | :---: | -| Java | [Spotless](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos%20%3CgroupId%3Ecom.diffplug.spotless%3C%2FgroupId%3E&type=code) | [JUnit](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/tree/main/java-demo/src/test) | [N/A](## "Java is a statically typed language.") | N/A | [Docker](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/java-demo/Dockerfile) + [Compose](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/java-demo/compose.yaml) | [GitHub Packages](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/pkgs/container/amrit-repos%2Fjava%2Fapp) | [GitHub Actions](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3A.github%2Fworkflows%2Fjava-*.yml+&type=code) | -| Python | [Ruff](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos%20%5Btool.ruff%5D&type=code) + [Bandit](https://github.com/PyCQA/bandit) | [PyTest](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/example-python/tests/test_main.py) | [MyPy](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos%20%5Btestenv%3Atype%5D&type=code) | [Grype](https://github.com/anchore/grype) + [pip-audit](https://pypi.org/project/pip-audit/) | [Docker](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/example-python/Dockerfile) | [GitHub Packages](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/pkgs/container/amrit-repos%2Fpython%2Fapp) | [GitHub Actions](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3A.github%2Fworkflows%2Fpython-*.yaml+&type=code) | -| TypeScript | [ESLint](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/typescript-demo/.eslintrc.json) | [N/A](## "In future, we expect to use front-end testing frameworks such as Cypress.") | [Strict](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3Atypescript-demo%2Ftsconfig.json+%22strict%22%3A+&type=code) | [Bearer](https://docs.bearer.com/) | [Docker](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/typescript-demo/Dockerfile) | [GitHub Packages](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/pkgs/container/amrit-repos%2Ftypescript%2Fapp) | [GitHub Actions](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3A.github%2Fworkflows%2Fts-*.yml&type=code) | +| Java | [Spotless](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos%20%3CgroupId%3Ecom.diffplug.spotless%3C%2FgroupId%3E&type=code) | [JUnit](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/tree/main/java-demo/src/test) | [N/A](## "Java is a statically typed language.") | [Trivy](https://trivy.dev/v0.57/) | [Docker](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/java-demo/Dockerfile) + [Compose](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/java-demo/compose.yaml) | [GitHub Packages](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/pkgs/container/amrit-repos%2Fjava%2Fapp) | [GitHub Actions](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3A.github%2Fworkflows%2Fjava-*.yml+&type=code) | +| Python | [Ruff](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos%20%5Btool.ruff%5D&type=code) + [Bandit](https://github.com/PyCQA/bandit) | [PyTest](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/example-python/tests/test_main.py) | [MyPy](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos%20%5Btestenv%3Atype%5D&type=code) | [Grype](https://github.com/anchore/grype) + [pip-audit](https://pypi.org/project/pip-audit/) + [Trivy](https://trivy.dev/v0.57/) | [Docker](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/example-python/Dockerfile) | [GitHub Packages](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/pkgs/container/amrit-repos%2Fpython%2Fapp) | [GitHub Actions](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3A.github%2Fworkflows%2Fpython-*.yaml+&type=code) | +| TypeScript | [ESLint](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/typescript-demo/.eslintrc.json) | [N/A](## "In future, we expect to use front-end testing frameworks such as Cypress.") | [Strict](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3Atypescript-demo%2Ftsconfig.json+%22strict%22%3A+&type=code) | [Bearer](https://docs.bearer.com/) + [Trivy](https://trivy.dev/v0.57/) | [Docker](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/blob/main/typescript-demo/Dockerfile) | [GitHub Packages](https://github.com/British-Oceanographic-Data-Centre/amrit-repos/pkgs/container/amrit-repos%2Ftypescript%2Fapp) | [GitHub Actions](https://github.com/search?q=repo%3ABritish-Oceanographic-Data-Centre%2Famrit-repos+path%3A.github%2Fworkflows%2Fts-*.yml&type=code) | All of our chosen linting rules, tests, as well as package builds can be executed both locally on developer machines and in the cloud via GitHub Actions. Security checks can be executed via GitHub Actions. ### Java + +See [detailled documentation](./java-demo/README.md). + #### Spotless + [Spotless](https://github.com/diffplug/spotless) is a static analysis and formatting tool for multiple languages, including Java. Our Java example runs spotless via Maven. #### JUnit -[JUnit](https://junit.org/) is a testing framework for Java. The unit tests we have written for this example run automatically at build time. +[JUnit](https://junit.org/) is a testing framework for Java. The unit tests we have written for this example run automatically at build time. #### Maven +Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information. + ### Python + #### Ruff + [Ruff](https://github.com/astral-sh/ruff) is a static analysis and formatting tool for Python, serving as an aggregator of rules multiple analysis and formatting tools. Our Python example is subject to a customised collection of Ruff rules including (but not limited to) those from [Black](https://black.readthedocs.io/en/stable/), [PyLint](https://pylint.readthedocs.io/en/latest/), and [Flake8](https://github.com/pycqa/flake8). Our Ruff rules are evaluated via Tox. #### Bandit + [Bandit](https://github.com/PyCQA/bandit) is a tool designed to find common security issues in Python code. It is worth noting that Ruff implements a subset of Bandit checks, however we have disabled these in preference of explicitly using Bandit itself to perform these. #### PyTest + [PyTest](https://docs.pytest.org/en/stable/) is a testing framework for Python. The unit tests we have written for this example are run via Tox. #### MyPy + [MyPy](https://github.com/python/mypy) is a static type checker for Python. It is run against our example code via Tox. #### Tox + [Tox](https://tox.wiki/en/4.23.2/) is a tool for automating the application of tests and other jobs against Python code. #### Grype + [Grype](https://github.com/anchore/grype) is a vulnerability scanner for container images and filesystems. #### Pip Audit + [pip-audit](https://pypi.org/project/pip-audit/) is a tool for scanning Python environments for packages with known vulnerabilities. ### TypeScript + +See [detailled documentation](./typescript-demo/README.md). + #### ESLint + [ESLint](https://eslint.org/) is a static analysis and formatting tool for JavaScript with official plugins supporting NextJS and TypeScript. #### Strict mode + TypeScript features optional static typing via [strict mode](https://www.typescriptlang.org/tsconfig/#strict), which has been enabled for our example code. ### Security scans -A tool called Bearer is performing these; there is a GitHub action which, on a pull request, will scan all the files within the typescript-demo folder. -A developer can find the latest instructions on installing and running Bearer locally [here](https://docs.bearer.com/reference/installation/). +A tool called Bearer is performing these; there is a GitHub action which, on a pull request, will scan all the files within the typescript-demo folder. + +A developer can find the latest instructions on installing and running Bearer locally [here](https://docs.bearer.com/reference/installation/). For quick reference, see the steps below. + ```shell $ curl -sfL https://raw.githubusercontent.com/Bearer/bearer/main/contrib/install.sh | sh # that will install locally into a ./bin/ folder - either keep it here or more it into a $path location @@ -75,13 +99,17 @@ $ curl -sfL https://raw.githubusercontent.com/Bearer/bearer/main/contrib/install # making sure your are in the root of the AMRIT project $ bearer scan typescript-demo ``` + ### Docker and Docker Compose + [Docker](https://www.docker.com/) is a tool for the development and execution of OCI-standard container images. It is used to build and run images for our Java, Python, and TypeScript example applications. [Docker Compose](https://docs.docker.com/compose/) is a tool for the orchestration of simple container-based applications, using declarative configuration files to manage single or multi-image applications that may be made up of multiple discrete services or single, self-sufficient images. It is used to run our Java example application. ### GitHub Packages + [GitHub Packages](https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages) is a software package hosting service that is tightly integrated with GitHub's ecosystem. By hosting our container images here, we make the process of downloading and running these example applications trivial: + ```console $ docker run -it -p 3000:3000 ghcr.io/british-oceanographic-data-centre/amrit-repos/typescript/app:v0.0.1 Unable to find image 'ghcr.io/british-oceanographic-data-centre/amrit-repos/typescript/app:v0.0.1' locally @@ -104,11 +132,13 @@ Status: Downloaded newer image for ghcr.io/british-oceanographic-data-centre/amr ✓ Starting... ✓ Ready in 58ms ``` + ```console -$ firefox http://localhost:3000 +firefox http://localhost:3000 ``` -![image](https://github.com/user-attachments/assets/87cdc725-f981-400b-ad86-e8351fb2af2d) +![image](https://github.com/user-attachments/assets/87cdc725-f981-400b-ad86-e8351fb2af2d) ### GitHub Actions + [GitHub Actions](https://github.com/features/actions) is a CI/CD service that allows developers to run customised jobs for building, testing, and deploying code in a variety of different ways. Our example code is tested and built in the cloud via GitHub actions, then automatically included in container image builds that are published to the GitHub Packages namespace associated with this repository whenever a new release is created. diff --git a/example-python/Dockerfile b/example-python/Dockerfile index e208190..3eab64e 100644 --- a/example-python/Dockerfile +++ b/example-python/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12 AS base +FROM python:3.12-slim AS base # Common base image. LABEL org.opencontainers.image.authors="matcaz@noc.ac.uk" LABEL org.opencontainers.image.description="Example OCI image for a Python application." @@ -11,8 +11,13 @@ LABEL org.opencontainers.image.vendor="Advance Marine Research Infrastructures T # Install Poetry for dependency management. ENV POETRY_HOME=/opt/poetry ENV PATH="${POETRY_HOME}/bin:${PATH}" -RUN mkdir -p /opt/poetry/project /opt/poetry/bin -RUN curl -sSL https://install.python-poetry.org | python - + +RUN \ + apt -y update && \ + apt -y install curl && \ + apt clean && \ + mkdir -p /opt/poetry/project /opt/poetry/bin && \ + curl -sSL https://install.python-poetry.org | python - # Install dependencies. COPY pyproject.toml poetry*.lock README.md /opt/poetry/project/