diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..81c7b58 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..36014cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'enhancement' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/actions/container-structure-test/Dockerfile b/.github/actions/container-structure-test/Dockerfile new file mode 100644 index 0000000..b2e735f --- /dev/null +++ b/.github/actions/container-structure-test/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine:3.18 + +# Get VERSION from: https://github.com/GoogleContainerTools/container-structure-test/releases +ARG VERSION=latest + +RUN apk add --no-cache curl + +# Get command from: https://github.com/GoogleContainerTools/container-structure-test#linux +RUN curl -LO https://storage.googleapis.com/container-structure-test/${VERSION}/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test diff --git a/.github/actions/container-structure-test/action.yml b/.github/actions/container-structure-test/action.yml new file mode 100644 index 0000000..651438e --- /dev/null +++ b/.github/actions/container-structure-test/action.yml @@ -0,0 +1,25 @@ +--- +name: Container Structure Tests +description: | + Container Structure Tests provide a powerful framework to validate the structure of a container image. + These tests can be used to check the output of commands in an image, as well as verify metadata and contents of the filesystem. + See https://github.com/GoogleContainerTools/container-structure-test + Note: This action does not pull remote images +inputs: + image: + description: Container Image to test + required: true + configFile: + description: Path to Container Structure Test Configuration File + required: false + default: default-container-structure-test.yaml +runs: + using: docker + image: Dockerfile + args: + - container-structure-test + - test + - --image + - ${{ inputs.image }} + - --config + - ${{ inputs.configFile }} diff --git a/.github/actions/container-structure-test/default-container-structure-test.yaml b/.github/actions/container-structure-test/default-container-structure-test.yaml new file mode 100644 index 0000000..3f9dd4f --- /dev/null +++ b/.github/actions/container-structure-test/default-container-structure-test.yaml @@ -0,0 +1,15 @@ +--- +# Run command: +# > container-structure-test test --image demoapp-frontend --config container-structure-test.yaml +schemaVersion: 2.0.0 + +metadataTest: + user: '' + +fileExistenceTests: + - name: Check Container structure test is installed + path: /usr/local/bin/container-structure-test + shouldExist: true + permissions: -rwxr-xr-x + uid: 0 + gid: 0 diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..54e11f8 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,67 @@ +--- +# actions/labeler configuration: https://github.com/marketplace/actions/labeler + +# Default GitHub Labels: https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels#about-default-labels + +# Add `documentation` label +documentation: + # to any changes of markdown files on any folder or subfolders + - '**/*.md' + # to any changes within docs folder + - docs/** + +# Add `javascript` label +javascript: + # to any changes within `src` folder + - src/** + # to any changes within `public` folder + - public/** + +# Add `dependencies` label +dependencies: + - package.json + - package-lock.json + + +# Add `container` label +container: + # to any changes of any Containerfile within this repository + - '**/Containerfile*' + # to any changes of any Dockerfile within this repository + - '**/Dockerfile*' + # to any chages to container-structure-test.yaml file + - container-structure-test.yaml + # to any chages to compose.yaml or compose.yml file + - compose.yaml + - compose.yml + +# Add `devcontainer` label +devcontainer: + # to any changes within .devcontainer folder + - .devcontainer/** + +# Add `github-workflow` label +github-workflow: + # to any changes within .github folder + - .github/** + +# Add `vscode-settings` label +vscode-settings: + # to any changes within .vscode folder + - .vscode/** + +# Add `git-config` label +git-config: + # to any changes within .git folder + - .git/** + # to any changes to .gitattributes file + - .gitattributes + # to any changes to .gitignore file + - .gitignore + # to any changes within .githooks folder + - .githooks/** + +# Add `lint` label +lint: + # to any changes to files, folders and subfolders with `lint` keyword + - '**lint**' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..ad2df8c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ + +Brief description of why this PR is necessary and/or what this PR solves. + +- Fixes [ISSUE #1] +- Fixes [ISSUE #2] + +Checklist: +* [ ] The title of this PR states what changed and the related issues number (used for the release note). +* [ ] The description of this PR includes a brief description of why this PR is necessary and/or what this PR solves. +* [ ] The description of this PR includes "Fixes [ISSUE #]" to automatically close associated issues. +* [ ] The changes in this PR are accompanied by documentation. +* [ ] The changes in this PR include unit and/or e2e tests. PRs without these are unlikely to be merged. diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..2c772e3 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,45 @@ +--- +# Configuration for .github/workflows/release-drafter.yml + +name-template: 'v$RESOLVED_VERSION 📢' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + # label: 'chore' + labels: + - documentation # label from .github/labeler.yml +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + - java # label from .github/labeler.yml + minor: + labels: + - 'minor' + - dependencies # label from .github/labeler.yml + - npm # label from .github/labeler.yml + - gradle # label from .github/labeler.yml + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + + $CHANGES + + ## Install from the command line + ```sh + DEMOAPP_BACKEND_IMAGE="ghcr.io/$OWNER/$REPOSITORY:v$RESOLVED_VERSION" docker compose --project-directory deploy/docker-compose up + ``` diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..5c7c810 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,41 @@ +--- +# Setup automatically generated release notes +# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes + +changelog: + exclude: + labels: + - ignore-for-release + categories: + - title: Breaking Changes 🛠 + labels: + - Semver-Major + - breaking-change + - title: Exciting New Features 🎉 + labels: + - Semver-Minor + - enhancement + - title: Documentation Improvements + labels: + - documentation + - title: Java Changes + labels: + - java + - title: Dependency Changes + labels: + - dependencies + - npm + - gradle + - title: Container Changes + labels: + - container + - title: Local Development Changes + labels: + - devcontainer + - vscode-settings + - title: Workflow Changes + labels: + - github-workflow + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..dd79381 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,193 @@ +--- +# Workflow syntax for GitHub Actions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +# Build Application and Upload Container Image to Docker Hub +name: Build and Scan Image + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Run workflow on push except for ignored branches and paths + push: + # Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot + branches-ignore: + # - 'dependabot/**' + - 'cherry-pick-*' + paths-ignore: + - '**.md' # Ignore documentation changes + - '.github/**(!build.yml)' # Ignore other workflow changes + # Run workflow on pull request + pull_request: # By default, a workflow only runs when a pull_request event's activity type is opened, synchronize, or reopened + # Allow user to manually trigger Workflow execution + workflow_dispatch: + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: read + +# Run a single job at a time: https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Set Workflow-level environment variables +env: + PROJECT: demoapp-frontend + +jobs: + build: + # Run job when not triggered by a merge + if: (github.event_name == 'push' && contains(toJSON(github.event.head_commit.message), 'Merge pull request ') == false) || (github.event_name != 'push') + runs-on: ubuntu-latest + environment: docker-hub # Use `docker-hub` repository environment + # Uncomment lines below to run `build` job on container + # Note: container image must contains commands required for step execution, e.g. docker, gzip, etc. + # container: + # image: mcr.microsoft.com/openjdk/jdk:17-ubuntu # Image Java version must match with `project.version` in pom.xml + # # Set credentials when container registry requires authentication to pull the image + # # credentials: + # # username: ${{ github.actor }} + # # password: ${{ secrets.github_token }} + steps: + # Workaround for the absence of github.branch_name + # Setting an environment variable: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + - name: Set VERSION + if: github.head_ref != '' + run: | + echo "VERSION=${{ github.head_ref }}" >> $GITHUB_ENV + - name: Set VERSION + if: github.head_ref == '' + run: | + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + + # Set Complete Container Image URL + - name: Set CONTAINER_IMAGE_URL + run: | + echo "CONTAINER_IMAGE_URL=${{ vars.DOCKER_REGISTRY_URL }}/${{ vars.DOCKER_REPOSITORY }}/${{ env.PROJECT }}:${{ env.VERSION }}" >> $GITHUB_ENV + + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + + # Cache NPM dependencies + - name: Install and Cache NPM dependencies + id: cache + uses: actions/cache@v3 # https://github.com/marketplace/actions/cache#using-a-combination-of-restore-and-save-actions + with: + path: | + ~/.npm + key: npm-${{ hashFiles('package.json','**/package-lock.json') }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 # https://github.com/marketplace/actions/docker-setup-build + + - name: Login to DockerHub + uses: docker/login-action@v3 # https://github.com/marketplace/actions/docker-login + with: + registry: ${{ vars.DOCKER_REGISTRY_URL }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + + - name: Docker Build and Push + uses: docker/build-push-action@v5 # https://github.com/marketplace/actions/build-and-push-docker-images + with: + context: . + file: Containerfile + push: true + tags: ${{ env.CONTAINER_IMAGE_URL }} # CONTAINER_IMAGE_URL is defined in GITHUB_ENV + cache-from: type=gha + cache-to: type=gha,mode=max + + container-structure-test: + needs: build + runs-on: ubuntu-latest + environment: docker-hub # Use `docker-hub` repository environment + steps: + # Workaround for the absence of github.branch_name + # Setting an environment variable: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + - name: Set VERSION + if: github.head_ref != '' + run: | + echo "VERSION=${{ github.head_ref }}" >> $GITHUB_ENV + - name: Set VERSION + if: github.head_ref == '' + run: | + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + + # Set Complete Container Image URL + - name: Set CONTAINER_IMAGE_URL + run: | + echo "CONTAINER_IMAGE_URL=${{ vars.DOCKER_REGISTRY_URL }}/${{ vars.DOCKER_REPOSITORY }}/${{ env.PROJECT }}:${{ env.VERSION }}" >> $GITHUB_ENV + + - name: Login to DockerHub + uses: docker/login-action@v3 # https://github.com/marketplace/actions/docker-login + with: + registry: ${{ vars.DOCKER_REGISTRY_URL }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + - name: Pull Container Image + # CONTAINER_IMAGE_URL is defined in GITHUB_ENV + run: | + docker pull ${{ env.CONTAINER_IMAGE_URL }} + + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + + - name: Run Container Structure Test + uses: ./.github/actions/container-structure-test + with: + image: ${{ env.CONTAINER_IMAGE_URL }} # CONTAINER_IMAGE_URL is defined in GITHUB_ENV + configFile: ./container-structure-test.yaml + + scan: + needs: build + runs-on: ubuntu-latest + # Set Job-level permissions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idpermissions + permissions: + security-events: write # Allow Job to upload scan results to GitHub + environment: docker-hub # Use `docker-hub` repository environment + env: + TRIVY_CACHE_DIR: /tmp/trivy/ + steps: + # Workaround for the absence of github.branch_name + # Setting an environment variable: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + - name: Set VERSION + if: github.head_ref != '' + run: | + echo "VERSION=${{ github.head_ref }}" >> $GITHUB_ENV + - name: Set VERSION + if: github.head_ref == '' + run: | + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + + # Set Complete Container Image URL + - name: Set CONTAINER_IMAGE_URL + run: | + echo "CONTAINER_IMAGE_URL=${{ vars.DOCKER_REGISTRY_URL }}/${{ vars.DOCKER_REPOSITORY }}/${{ env.PROJECT }}:${{ env.VERSION }}" >> $GITHUB_ENV + + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + + - name: Cache Trivy + id: cache + uses: actions/cache@v3 # https://github.com/marketplace/actions/cache#using-a-combination-of-restore-and-save-actions + with: + path: ${{ env.TRIVY_CACHE_DIR }} + key: trivy-${{ hashFiles('**/pom.xml', '**/Containerfile*') }} # Trivy scan results are influenced by npm dependencies and Containerfile runtime image + + - name: Scan Image with Aqua Security Trivy + uses: aquasecurity/trivy-action@0.13.0 # https://github.com/marketplace/actions/aqua-security-trivy + with: + image-ref: ${{ env.CONTAINER_IMAGE_URL }} # CONTAINER_IMAGE_URL is defined in GITHUB_ENV + vuln-type: 'os,library' + severity: 'LOW,MEDIUM,HIGH,CRITICAL' + scanners: 'vuln,secret,config' + ignore-unfixed: true + exit-code: '1' + cache-dir: ${{ env.TRIVY_CACHE_DIR }} + format: sarif + output: 'trivy-results.sarif' + env: + TRIVY_USERNAME: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + TRIVY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2.22.5 # https://github.com/github/codeql-action/tree/main/upload-sarif + with: + sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/code-scan-codecov.yml b/.github/workflows/code-scan-codecov.yml new file mode 100644 index 0000000..7d0655e --- /dev/null +++ b/.github/workflows/code-scan-codecov.yml @@ -0,0 +1,40 @@ +--- +# Workflow syntax for GitHub Actions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +name: Scan Code with CodeCov + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Run workflow on push except for ignored branches and paths + push: + # Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot + branches-ignore: + - 'dependabot/**' + - 'cherry-pick-*' + paths-ignore: + - '**.md' # Ignore documentation changes + - '.github/**(!code-scan-codecov.yml)' # Ignore other workflow changes + # Run workflow on pull request + pull_request: # By default, a workflow only runs when a pull_request event's activity type is opened, synchronize, or reopened + +# Run a single job at a time: https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: read + +jobs: + codecov: + # Run job when not triggered by a merge + if: (github.event_name == 'push' && contains(toJSON(github.event.head_commit.message), 'Merge pull request ') == false) || (github.event_name != 'push') + runs-on: ubuntu-latest # GitHub-hosted runners: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + steps: + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/code-scan-codeql.yml b/.github/workflows/code-scan-codeql.yml new file mode 100644 index 0000000..652e8be --- /dev/null +++ b/.github/workflows/code-scan-codeql.yml @@ -0,0 +1,60 @@ +--- +# Workflow syntax for GitHub Actions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +# See CodeQL results at https://github.com/paul-gilber/demoapp-frontend/security/code-scanning/tools/CodeQL/status/ +name: Scan Code with CodeQL + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Run workflow on push except for ignored branches and paths + push: + # Secrets aren't available for dependabot on push. https://docs.github.com/en/enterprise-cloud@latest/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/troubleshooting-the-codeql-workflow#error-403-resource-not-accessible-by-integration-when-using-dependabot + branches-ignore: + - 'dependabot/**' + - 'cherry-pick-*' + paths-ignore: + - '**.md' # Ignore documentation changes + - '.github/**(!code-scan-codeql.yml)' # Ignore other workflow changes + # Run workflow on pull request + pull_request: # By default, a workflow only runs when a pull_request event's activity type is opened, synchronize, or reopened +# Run a single job at a time: https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: read + +jobs: + codeql: + # Run job when not triggered by a merge + if: (github.event_name == 'push' && contains(toJSON(github.event.head_commit.message), 'Merge pull request ') == false) || (github.event_name != 'push') + runs-on: ubuntu-latest # GitHub-hosted runners: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/autobuild to send a status report + steps: + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + + # Initializes CodeQL scanning tools + # https://github.com/github/codeql-action + - name: Initialize CodeQL + uses: github/codeql-action/init@v2.22.5 # https://github.com/github/codeql-action + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # id: autobuild + # uses: github/codeql-action/autobuild@v2.22.5 + + # Note: If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + - name: Build + run: | + npm ci + + - name: Run CodeQL Analysis + uses: github/codeql-action/analyze@v2.22.5 diff --git a/.github/workflows/code-scan-sonarcloud.yml b/.github/workflows/code-scan-sonarcloud.yml new file mode 100644 index 0000000..697e0aa --- /dev/null +++ b/.github/workflows/code-scan-sonarcloud.yml @@ -0,0 +1,156 @@ +--- +# Workflow syntax for GitHub Actions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +# SonarCloud: https://sonarcloud.io/ +# CI analysis while Automatic Analysis must be disabled for successful execution of this workflow https://docs.sonarcloud.io/advanced-setup/automatic-analysis/#conflict-with-ci-based-analysis +name: Scan Code with SonarCloud + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Run workflow on push except for ignored branches and paths + push: + paths-ignore: + - '**.md' # Ignore documentation changes + - '.github/**(!code-scan-sonarcloud.yml)' # Ignore other workflow changes + # Run workflow on pull request + pull_request: # By default, a workflow only runs when a pull_request event's activity type is opened, synchronize, or reopened + +# Run a single job at a time: https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: read + +jobs: + sonarcloud: + # Run job when not triggered by a merge + if: (github.event_name == 'push' && contains(toJSON(github.event.head_commit.message), 'Merge pull request ') == false) || (github.event_name != 'push') + runs-on: ubuntu-latest # GitHub-hosted runners: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + # Set Job-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs + permissions: + pull-requests: read # Allow SonarCloud to get pull request details + environment: sonarcloud # Use `sonarcloud` repository environment + steps: + # Workaround for the absence of github.branch_name + # Setting an environment variable: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + - name: Set VERSION + if: github.head_ref != '' + run: | + echo "VERSION=${{ github.head_ref }}" >> $GITHUB_ENV + - name: Set VERSION + if: github.head_ref == '' + run: | + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + with: + # Disabling shallow clone is recommended for improving relevancy of reporting + fetch-depth: 0 + + # Setup Java + - uses: actions/setup-java@v3 # https://github.com/actions/setup-java + with: + distribution: microsoft # Microsoft was selected to match Visual Studio Code Dev Container Java distribuition, see .devcontainer/devcontainer.json. Supported distributions: https://github.com/actions/setup-java#supported-distributions + java-version: '17' # Java version must match `project.properties['java.version']` in pom.xml + + - name: Cache NPM dependencies + uses: actions/cache@v3 # https://github.com/marketplace/actions/cache#using-a-combination-of-restore-and-save-actions + with: + path: | + ~/.npm + key: npm-${{ hashFiles('**/pom.xml') }} + + - name: Cache SonarCloud dependencies + uses: actions/cache@v3 # https://github.com/marketplace/actions/cache#using-a-combination-of-restore-and-save-actions + with: + path: | + ~/.sonar/cache + key: sonarcloud-${{ github.repository_id }} + + - name: SonarCloud Scan via Github Action + uses: sonarsource/sonarcloud-github-action@v2.0.2 # https://github.com/marketplace/actions/sonarcloud-scan + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GITHUB_TOKEN is a special secret automatically generated by GitHub: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # SONAR_TOKEN must be defined in `sonarcloud` repository environment. SonarCloud access token should be generated from https://sonarcloud.io/account/security/ + + # In case you need to override default settings + # - name: Analyze with SonarCloud + # uses: sonarsource/sonarcloud-github-action@v2.0.2 + # with: + # projectBaseDir: my-custom-directory + # args: > + # -Dsonar.organization=my-organization + # -Dsonar.projectKey=my-projectkey + # -Dsonar.python.coverage.reportPaths=coverage.xml + # -Dsonar.sources=lib/ + # -Dsonar.test.exclusions=tests/** + # -Dsonar.tests=tests/ + # -Dsonar.verbose=true + + # # SonarCloud GitHub Action fails when a NPM project is detected and recommends usage of NPM Sonar plugin + # - name: SonarCloud Scan via NPM (${{ github.event_name }}) + # if: github.event_name != 'pull_request' + # # Get SONAR_ORGANIZATION and SONAR_PROJECT_KEY from https://sonarcloud.io/project/information?id=paul-gilber_demoapp-frontend + # # SONAR_ORGANIZATION must be defined in `sonarcloud` repository environment + # # SONAR_PROJECT_KEY must be defined in `sonarcloud` repository environment + # run: | + # mvn -B verify \ + # org.sonarsource.scanner.npm:sonar-npm-plugin:sonar \ + # -Drevision=${{ env.VERSION }} \ + # -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} \ + # -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \ + # -Dmaven.test.skip=true \ + # -Ddockerfile.skip=true + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GITHUB_TOKEN is a special secret automatically generated by GitHub: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret + # SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} # SONAR_HOST_URL must be defined in `sonarcloud` repository environment + # # SONAR_ORGANIZATION: ${{ vars.SONAR_ORGANIZATION }} # SONAR_ORGANIZATION must be defined in `sonarcloud` repository environment + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # SONAR_TOKEN must be defined in `sonarcloud` repository environment. SonarCloud access token should be generated from https://sonarcloud.io/account/security/ + + # # SonarCloud GitHub Action fails when a NPM project is detected and recommends usage of NPM Sonar plugin + # - name: SonarCloud Scan via NPM (pull_request) + # if: github.event_name == 'pull_request' + # # Get SONAR_ORGANIZATION and SONAR_PROJECT_KEY from https://sonarcloud.io/project/information?id=paul-gilber_demoapp-frontend + # # SONAR_ORGANIZATION must be defined in `sonarcloud` repository environment + # # SONAR_PROJECT_KEY must be defined in `sonarcloud` repository environment + # run: | + # mvn -B verify \ + # org.sonarsource.scanner.npm:sonar-npm-plugin:sonar \ + # -Drevision=${{ env.VERSION }} \ + # -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} \ + # -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \ + # -Dsonar.pullrequest.provider=GitHub \ + # -Dsonar.pullrequest.github.repository=${{ github.repository }} \ + # -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} \ + # -Dsonar.pullrequest.branch=${{ github.head_ref }} \ + # -Dsonar.pullrequest.base=${{ github.base_ref }} \ + # -Dmaven.test.skip=true \ + # -Ddockerfile.skip=true + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GITHUB_TOKEN is a special secret automatically generated by GitHub: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret + # SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} # SONAR_HOST_URL must be defined in `sonarcloud` repository environment + # # SONAR_ORGANIZATION: ${{ vars.SONAR_ORGANIZATION }} # SONAR_ORGANIZATION must be defined in `sonarcloud` repository environment + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # SONAR_TOKEN must be defined in `sonarcloud` repository environment. SonarCloud access token should be generated from https://sonarcloud.io/account/security/ + + # In case you need to override default settings + # - name: SonarCloud Scan via NPM + # run: | + # mvn -B verify \ + # org.sonarsource.scanner.npm:sonar-npm-plugin:sonar \ + # -Dmaven.test.skip=true \ + # -Ddockerfile.skip=true \ + # -Dsonar.organization=my-organization \ + # -Dsonar.projectKey=my-projectkey \ + # -Dsonar.python.coverage.reportPaths=coverage.xml \ + # -Dsonar.sources=lib/ \ + # -Dsonar.test.exclusions=tests/** \ + # -Dsonar.tests=tests/ \ + # -Dsonar.verbose=true + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GITHUB_TOKEN is a special secret automatically generated by GitHub: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret + # SONAR_ORGANIZATION: ${{ vars.SONAR_ORGANIZATION }} # SONAR_ORGANIZATION must be defined in `sonarcloud` repository environment + # SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }} # SONAR_HOST_URL must be defined in `sonarcloud` repository environment + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # SONAR_TOKEN must be defined in `sonarcloud` repository environment. SonarCloud access token should be generated from https://sonarcloud.io/account/security/ diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..819c29a --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,25 @@ +--- +name: Pull Request Labeler + +# Events that trigger workflows: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + pull_request: # By default, a workflow only runs when a pull_request event's activity type is opened, synchronize, or reopened + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: read + +jobs: + labeler: + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + + - uses: actions/labeler@v4 + with: + configuration-path: ./.github/labeler.yml + sync-labels: true # Whether or not to remove labels when matching files are reverted or no longer changed by the PR + dot: true # Whether or not to auto-include paths starting with dot (e.g. .github) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..5ce328f --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,39 @@ +--- +# Workflow syntax for GitHub Actions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +name: Lint Files + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Run workflow on push except for ignored branches and paths + push: + branches-ignore: + - 'cherry-pick-*' + paths: + - '**.yaml' + - '**.yml' + - '**.yamllint' + # Run workflow on pull request + pull_request: # By default, a workflow only runs when a pull_request event's activity type is opened, synchronize, or reopened + + # Allow other Workflows to call this Workflow + workflow_call: + +# Run a single job at a time: https://docs.github.com/en/actions/using-jobs/using-concurrency +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: + contents: read + +jobs: + lint: + # Run job when not triggered by a merge + if: (github.event_name == 'push' && contains(toJSON(github.event.head_commit.message), 'Merge pull request ') == false) || (github.event_name != 'push') + runs-on: ubuntu-latest # GitHub-hosted runners: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + steps: + - name: Checkout repository + uses: actions/checkout@v4 # https://github.com/marketplace/actions/checkout + - name: YAML Lint + run: yamllint . # yamllint is pre-installed: https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md#tools diff --git a/.github/workflows/merge-cleanup.yml b/.github/workflows/merge-cleanup.yml new file mode 100644 index 0000000..9fc22d4 --- /dev/null +++ b/.github/workflows/merge-cleanup.yml @@ -0,0 +1,78 @@ +--- +# Workflow syntax for GitHub Actions: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions +name: Merge Cleanup + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Run Workflow upon pull request + pull_request: + types: [closed] + + # Allow user to manually trigger Workflow execution + workflow_dispatch: + + # Run Workflow upon pull request to specified target branch(es) + # pull_request_target: + # types: [closed] + # branches: + # - main + +# Set Workflow-level permissions: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: {} # This Workflow does not require any permission + +# Set Workflow-level environment variables +env: + PROJECT: demoapp-frontend + +jobs: + output-information: + runs-on: ubuntu-latest + steps: + - name: Output Information + run: | + echo "${{ toJSON(github) }}" + + # Skopeo deletes image tag by reference which is not allowed by Docker Hub + # docker-hub-skopeo: + # runs-on: ubuntu-latest + # environment: docker-hub + # # Skopeo is pre-installed in GitHub hosted runners: https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md#tools + # - name: Skopeo Login + # run: | + # skopeo login ${{ vars.DOCKER_REGISTRY_URL }} \ + # --username ${{ secrets.DOCKER_REGISTRY_USERNAME }} \ + # --password ${{ secrets.DOCKER_REGISTRY_PASSWORD }} \ + # - name: Skopeo Delete Image + # id: skopeo-delete + # run: | + # skopeo delete docker://${{ vars.DOCKER_REGISTRY_URL }}/${{ vars.DOCKER_REPOSITORY }}/${{ env.PROJECT }}:${{ env.VERSION }} + + # Regctl allows image tag deletion which is allowed by Docker Hub + docker-hub-regctl: + runs-on: ubuntu-latest + environment: docker-hub + steps: + # Workaround for the absence of github.branch_name + # Setting an environment variable: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + - name: Set VERSION + if: github.head_ref != '' + run: | + echo "VERSION=${{ github.head_ref }}" >> $GITHUB_ENV + - name: Set VERSION + if: github.head_ref == '' + run: | + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_ENV + + # Install regctl: https://github.com/regclient/regclient + - name: Install regctl + run: | + curl -L https://github.com/regclient/regclient/releases/latest/download/regctl-linux-amd64 > regctl + chmod 755 regctl + - name: regctl login + run: | + ./regctl registry login ${{ vars.DOCKER_REGISTRY_URL }} \ + --user ${{ secrets.DOCKER_REGISTRY_USERNAME }} \ + --pass ${{ secrets.DOCKER_REGISTRY_PASSWORD }} \ + - name: regctl Delete Image Tag + run: | + ./regctl tag delete ${{ vars.DOCKER_REGISTRY_URL }}/${{ vars.DOCKER_REPOSITORY }}/${{ env.PROJECT }}:${{ env.VERSION }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..47d7e95 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,43 @@ +--- +# Release Drafter: https://github.com/marketplace/actions/release-drafter +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - main + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + # pull_request_target event is required for autolabeler to support PRs from forks + # pull_request_target: + # types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # (Optional) GitHub Enterprise requires GHE_HOST variable set + # - name: Set GHE_HOST + # run: | + # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV + + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + # with: + # config-name: my-config.yml + # disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2a75251 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +--- +# Publishing Docker images: https://docs.github.com/en/actions/publishing-packages/publishing-docker-images +# Automatically generated release notes https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes +name: Publish Container Image + +# Events: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +on: + # Trigger Workflow on push event + push: + branches: + - main # When `latest` image tag is published when default branch is update + tags: + - 'v*' # When a tag starting with `v` is created e.g. v1.0.0 + +# Set Workflow-level environment variables +env: + PROJECT: demoapp-frontend + +jobs: + push_to_registries: + name: Push Container image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 # https://github.com/marketplace/actions/docker-setup-build + + - name: Log in to the Container registry + uses: docker/login-action@v3 # https://github.com/marketplace/actions/docker-login + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 # https://github.com/marketplace/actions/docker-metadata-action + with: + images: | + ghcr.io/${{ github.repository }} + tags: | + # set latest tag for default branch + type=raw,value=latest,enable={{is_default_branch}} + # branch event + type=ref,event=branch + # dynamically set the branch name and tag as a prefix (short sha format) + type=sha,prefix={{branch}}{{tag}}- + # tag event + type=ref,event=tag + + # Docker Buildx Bake supports multi-platform builds: https://docs.docker.com/build/bake/ + - name: Docker Build and Push (Bake) + env: + BUILD_IMAGE: registry.access.redhat.com/ubi8/nodejs-18 + RUNTIME_IMAGE: registry.access.redhat.com/ubi8/nodejs-18 + uses: docker/bake-action@v4 # https://github.com/marketplace/actions/docker-buildx-bake + with: + files: | + ./docker-bake.hcl + ${{ steps.meta.outputs.bake-file }} + set: | + build.args.BUILD_IMAGE=${{ env.BUILD_IMAGE }} + build.args.RUNTIME_IMAGE=${{ env.RUNTIME_IMAGE }} + targets: gha-build + push: true diff --git a/.yamllint b/.yamllint index 11e6a66..8383e4c 100644 --- a/.yamllint +++ b/.yamllint @@ -4,6 +4,9 @@ yaml-files: - '*.yml' - '.yamllint' +ignore: + - node_modules/ + rules: anchors: enable braces: enable diff --git a/Containerfile b/Containerfile index 41a0383..41cbf7e 100644 --- a/Containerfile +++ b/Containerfile @@ -3,13 +3,13 @@ ARG RUNTIME_IMAGE="registry.access.redhat.com/ubi8/nodejs-18" # Build FROM ${BUILD_IMAGE} as build -WORKDIR /app +WORKDIR /build -COPY --chown=default:default package.json ./package.json -COPY --chown=default:default public ./public -COPY --chown=default:default src ./src +COPY package.json ./package.json +COPY public ./public +COPY src ./src -RUN npm install --silent \ +RUN npm install \ && npm run build @@ -17,7 +17,7 @@ RUN npm install --silent \ FROM ${RUNTIME_IMAGE} WORKDIR /app -COPY --from=build --chown=default:default /app/build ./build +COPY --from=build --chown=default:default /build/build ./build RUN npm install -g serve CMD serve -n -s build -l tcp://0.0.0.0:8080 diff --git a/docker-bake.hcl b/docker-bake.hcl new file mode 100644 index 0000000..cad772c --- /dev/null +++ b/docker-bake.hcl @@ -0,0 +1,42 @@ +// Configuration for docker/bake-action: https://github.com/docker/bake-action +// https://github.com/marketplace/actions/docker-metadata-action#bake-definition +// docker-bake.hcl + +target "docker-metadata-action" {} + +// Create Base Build Target +target "build" { + inherits = ["docker-metadata-action"] + context = "./" + // Use multi-stage Containerfile + dockerfile = "Containerfile" +} + +// Create Platforms Target +target "platforms" { + // Set target platforms for multi-platform builds https://docs.docker.com/build/bake/reference/#targetplatforms + platforms = [ + "linux/amd64", + "linux/arm64" + ] +} + +// Create Multi Platform Build Target +target "multi-platform-build" { + inherits = ["build", "platforms"] +} + +// Create GitHub Action Cache Target +target "gha-cache" { + cache-from = [ + "type=gha" + ] + cache-to = [ + "type=gha,mode=max" + ] +} + +// Create GitHub Action Build Target +target "gha-build" { + inherits = ["multi-platform-build", "gha-cache"] +}