diff --git a/.github/openshift/openshift.deploy.yml b/.github/openshift/openshift.deploy.yml deleted file mode 100644 index 0d3779d4..00000000 --- a/.github/openshift/openshift.deploy.yml +++ /dev/null @@ -1,139 +0,0 @@ -apiVersion: template.openshift.io/v1 -kind: Template -labels: - app: ${NAME}-${COMPONENT} - app.kubernetes.io/part-of: ${NAME}-${COMPONENT} -parameters: - - name: NAME - description: Module name - value: nr-rating-curve-app - - name: COMPONENT - description: Component name - value: frontend - - name: IMAGE_TAG - description: Image tag to use - value: latest - - name: DOMAIN - value: apps.silver.devops.gov.bc.ca - - name: CPU_REQUEST - value: "25m" - - name: MEMORY_REQUEST - value: "50Mi" - - name: CPU_LIMIT - value: "75m" - - name: MEMORY_LIMIT - value: "150Mi" - - name: REGISTRY - description: Container registry to import from (internal is image-registry.openshift-image-registry.svc:5000) - value: ghcr.io - - name: PROMOTE - description: Image (namespace/name:tag) to promote/import - value: bcgov/nr-rating-curve-app/frontend:latest -objects: - - apiVersion: v1 - kind: ImageStream - metadata: - labels: - app: ${NAME}-${COMPONENT} - name: ${NAME}-${COMPONENT} - spec: - lookupPolicy: - local: false - tags: - - name: ${IMAGE_TAG} - from: - kind: DockerImage - name: ${REGISTRY}/${PROMOTE} - referencePolicy: - type: Local - - apiVersion: v1 - kind: DeploymentConfig - metadata: - labels: - app: ${NAME}-${COMPONENT} - name: ${NAME}-${COMPONENT} - spec: - replicas: 1 - triggers: - - type: ConfigChange - - type: ImageChange - imageChangeParams: - automatic: true - containerNames: - - ${NAME} - from: - kind: ImageStreamTag - name: ${NAME}-${COMPONENT}:${IMAGE_TAG} - selector: - deploymentconfig: ${NAME}-${COMPONENT} - strategy: - type: Rolling - template: - metadata: - labels: - app: ${NAME} - deploymentconfig: ${NAME}-${COMPONENT} - spec: - containers: - - image: ${NAME}/${COMPONENT}:${IMAGE_TAG} - imagePullPolicy: Always - name: ${NAME}-${COMPONENT} - ports: - - containerPort: 8003 - protocol: TCP - resources: - requests: - cpu: ${CPU_REQUEST} - memory: ${MEMORY_REQUEST} - limits: - cpu: ${CPU_LIMIT} - memory: ${MEMORY_LIMIT} - readinessProbe: - httpGet: - path: / - port: 8003 - scheme: HTTP - initialDelaySeconds: 15 - periodSeconds: 30 - timeoutSeconds: 1 - livenessProbe: - successThreshold: 1 - failureThreshold: 3 - httpGet: - path: / - port: 8003 - scheme: HTTP - initialDelaySeconds: 15 - periodSeconds: 30 - timeoutSeconds: 5 - - apiVersion: v1 - kind: Service - metadata: - labels: - app: ${NAME} - name: ${NAME}-${COMPONENT} - spec: - ports: - - name: 8003-tcp - protocol: TCP - port: 80 - targetPort: 8003 - selector: - deploymentconfig: ${NAME}-${COMPONENT} - - apiVersion: route.openshift.io/v1 - kind: Route - metadata: - labels: - app: ${NAME} - name: ${NAME}-${COMPONENT} - spec: - host: ${NAME}-${COMPONENT}.${DOMAIN} - port: - targetPort: 8003-tcp - to: - kind: Service - name: ${NAME}-${COMPONENT} - weight: 100 - tls: - termination: edge - insecureEdgeTerminationPolicy: Redirect diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml new file mode 100644 index 00000000..b4730565 --- /dev/null +++ b/.github/workflows/analysis.yml @@ -0,0 +1,61 @@ +name: Analysis + +on: + push: + branches: [main] + pull_request: + types: [opened, reopened, synchronize, ready_for_review, converted_to_draft] + schedule: + - cron: "0 11 * * 0" # 3 AM PST = 12 PM UDT, runs sundays + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + codeql: + name: CodeQL Security Scan + if: ${{ ! github.event.pull_request.draft }} + runs-on: ubuntu-22.04 + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v3 + with: + languages: javascript + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:javascript" + + # https://github.com/marketplace/actions/aqua-security-trivy + trivy: + name: Trivy Security Scan + if: ${{ ! github.event.pull_request.draft }} + runs-on: ubuntu-22.04 + timeout-minutes: 1 + steps: + - uses: actions/checkout@v4 + - name: Run Trivy vulnerability scanner in repo mode + uses: aquasecurity/trivy-action@0.18.0 + with: + format: "sarif" + output: "trivy-results.sarif" + ignore-unfixed: true + scan-type: "fs" + scanners: "vuln,secret,config" + severity: "CRITICAL,HIGH" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: "trivy-results.sarif" + + results: + name: Analysis Results + needs: [codeql, trivy] + runs-on: ubuntu-22.04 + steps: + - run: echo "Workflow completed successfully!" diff --git a/.github/workflows/merge-main.yml b/.github/workflows/merge-main.yml deleted file mode 100644 index e6235bde..00000000 --- a/.github/workflows/merge-main.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Merge to Main - -on: - push: - branches: - - main - paths-ignore: - - ".github/ISSUE_TEMPLATE/*" - - "**.md" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true - -jobs: - codeql: - name: Semantic Code Analysis - runs-on: ubuntu-22.04 - permissions: - actions: read - contents: read - security-events: write - steps: - - uses: actions/checkout@v4 - - - name: Initialize - uses: github/codeql-action/init@v3 - with: - languages: javascript - - # 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 - uses: github/codeql-action/autobuild@v3 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - - deploys-test: - name: TEST Deployments - needs: - - codeql - environment: test - runs-on: ubuntu-22.04 - strategy: - matrix: - name: [frontend,rctool] - include: - - name: frontend - file: frontend/openshift.nginx.deploy.yml - overwrite: true - - name: rctool - file: frontend/openshift.deploy.yml - overwrite: true - steps: - - uses: bcgov-nr/action-deployer-openshift@v2.1.0 - with: - file: ${{ matrix.file }} - oc_namespace: ${{ secrets.OC_NAMESPACE }} - oc_server: ${{ secrets.OC_SERVER }} - oc_token: ${{ secrets.OC_TOKEN }} - overwrite: ${{ matrix.overwrite }} - parameters: - -p ZONE=test -p PROMOTE=${{ github.repository }}/${{ matrix.name }}:test - -p NAME=${{ github.event.repository.name }} ${{ matrix.parameters }} - penetration_test: true diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 00000000..76fe198d --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,38 @@ +name: Merge + +on: + push: + branches: [main] + paths-ignore: + - ".github/ISSUE_TEMPLATE/*" + - "**.md" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +jobs: + deploys-test: + name: TEST Deployments + environment: test + runs-on: ubuntu-22.04 + strategy: + matrix: + name: [frontend, rctool] + include: + - name: frontend + file: frontend/openshift.nginx.deploy.yml + - name: rctool + file: frontend/openshift.deploy.yml + steps: + - uses: bcgov-nr/action-deployer-openshift@v2.1.0 + with: + file: ${{ matrix.file }} + oc_namespace: ${{ secrets.OC_NAMESPACE }} + oc_server: ${{ vars.OC_SERVER }} + oc_token: ${{ secrets.OC_TOKEN }} + overwrite: true + parameters: + -p ZONE=test -p PROMOTE=${{ github.repository }}/${{ matrix.name }}:test + -p NAME=${{ github.event.repository.name }} diff --git a/.github/workflows/pr-close.yml b/.github/workflows/pr-close.yml index d38865c2..fccaf927 100644 --- a/.github/workflows/pr-close.yml +++ b/.github/workflows/pr-close.yml @@ -1,43 +1,21 @@ -name: Pull Request Closed +name: PR Closed on: pull_request: - types: - - closed + types: [closed] concurrency: # PR open and close use the same group, allowing only one at a time - group: pr-${{ github.ref }} + group: ${{ github.event.number }} cancel-in-progress: true jobs: - # Clean up OpenShift when PR closed, no conditions - cleanup-openshift: - name: Cleanup OpenShift - runs-on: ubuntu-22.04 - steps: - - name: Remove OpenShift artifacts - run: | - oc login --token=${{ secrets.OC_TOKEN }} --server=${{ secrets.OC_SERVER }} - oc project ${{ secrets.OC_NAMESPACE }} - - # Remove old build runs, build pods and deployment pods - oc delete all,pvc,secret -l app=${{ github.event.repository.name }}-${{ github.event.number }} - - # If merged into main, then handle any image promotions - image-promotions: - name: Image Promotions - if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' - runs-on: ubuntu-22.04 - permissions: - packages: write - strategy: - matrix: - package: [frontend,rctool] - steps: - - uses: shrink/actions-docker-registry-tag@v4 - with: - registry: ghcr.io - repository: ${{ github.repository }}/${{ matrix.package }} - target: ${{ github.event.number }} - tags: test + cleanup: + name: Cleanup and Images + uses: bcgov/quickstart-openshift-helpers/.github/workflows/.pr-close.yml@v0.4.0 + secrets: + oc_namespace: ${{ secrets.OC_NAMESPACE }} + oc_token: ${{ secrets.OC_TOKEN }} + with: + cleanup: label + packages: frontend rctool diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index acc9513b..f3b5c982 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -5,35 +5,10 @@ on: concurrency: # PR open and close use the same group, allowing only one at a time - group: pr-${{ github.ref }} + group: ${{ github.event.number }} cancel-in-progress: true jobs: - pr-greeting: - name: PR Greeting - env: - DOMAIN: apps.silver.devops.gov.bc.ca - PREFIX: ${{ github.event.repository.name }}-${{ github.event.number }} - runs-on: ubuntu-22.04 - permissions: - pull-requests: write - steps: - - name: PR Greeting - uses: bcgov-nr/action-pr-description-add@v1.1.1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - add_markdown: | - --- - - Thanks for the PR! - - Any successful deployments (not always required) will be available below. - # [Backend](https://${{ env.PREFIX }}-backend.${{ env.DOMAIN }}/) available - [Frontend](https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }}/) available - - Once merged, code will be promoted and handed off to following workflow run. - [Main Merge Workflow](https://github.com/${{ github.repository }}/actions/workflows/merge-main.yml) - builds: name: Builds runs-on: ubuntu-22.04 @@ -41,32 +16,27 @@ jobs: packages: write strategy: matrix: - package: [frontend,rctool] + package: [frontend, rctool] include: - package: frontend - triggers: ('frontend/') build_file: ./frontend/Dockerfile.nginx - build_context: ./frontend - package: rctool - triggers: ('frontend/') build_file: ./frontend/Dockerfile.app - build_context: ./frontend steps: - uses: actions/checkout@v4 - uses: bcgov-nr/action-builder-ghcr@v2.0.2 with: package: ${{ matrix.package }} build_file: ${{ matrix.build_file }} - build_context: ${{ matrix.build_context }} + build_context: ./${{ matrix.package }} tag: ${{ github.event.number }} tag_fallback: test token: ${{ secrets.GITHUB_TOKEN }} - triggers: ${{ matrix.triggers }} + triggers: ('frontend/') deploys: name: Deploys - needs: - - builds + needs: [builds] runs-on: ubuntu-22.04 strategy: matrix: @@ -74,22 +44,23 @@ jobs: include: - name: frontend file: frontend/openshift.nginx.deploy.yml - overwrite: true - parameters: -p MIN_REPLICAS=1 -p MAX_REPLICAS=1 - name: rctool file: frontend/openshift.deploy.yml - overwrite: true - parameters: -p MIN_REPLICAS=1 -p MAX_REPLICAS=1 steps: - uses: bcgov-nr/action-deployer-openshift@v2.1.0 with: file: ${{ matrix.file }} oc_namespace: ${{ secrets.OC_NAMESPACE }} - oc_server: ${{ secrets.OC_SERVER }} + oc_server: ${{ vars.OC_SERVER }} oc_token: ${{ secrets.OC_TOKEN }} - overwrite: ${{ matrix.overwrite }} - penetration_test: false + overwrite: true parameters: - -p ZONE=${{ github.event.number }} -p NAME=${{ github.event.repository.name }} + -p MIN_REPLICAS=1 -p MAX_REPLICAS=1 -p ZONE=${{ github.event.number }} -p PROMOTE=${{ github.repository }}/${{ matrix.name }}:${{ github.event.number }} - ${{ matrix.parameters }} + + results: + name: PR Results + needs: [deploys] + runs-on: ubuntu-22.04 + steps: + - run: echo "Workflow completed successfully!" diff --git a/.github/workflows/pr-validate.yml b/.github/workflows/pr-validate.yml new file mode 100644 index 00000000..46359502 --- /dev/null +++ b/.github/workflows/pr-validate.yml @@ -0,0 +1,27 @@ +name: PR Validate + +on: + pull_request: + types: [edited, opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-edit-${{ github.event.number }} + cancel-in-progress: true + +jobs: + validate: + name: Validate PR + env: + DOMAIN: apps.silver.devops.gov.bc.ca + PREFIX: ${{ github.event.repository.name }}-${{ github.event.number }} + uses: bcgov/quickstart-openshift-helpers/.github/workflows/.pr-validate.yml@v0.5.0 + with: + markdown_links: | + [Frontend](https://${{ env.PREFIX }}-frontend.${{ env.DOMAIN }}/) available + + results: + name: Validate Results + needs: [validate] + runs-on: ubuntu-22.04 + steps: + - run: echo "Workflow completed successfully!" diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 1ff88f9e..0705dbb3 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -1,6 +1,8 @@ -name: Prod Deploy +name: PROD Release on: + release: + types: [published] workflow_dispatch: concurrency: @@ -8,6 +10,18 @@ concurrency: cancel-in-progress: true jobs: + vars: + name: Set Variables + outputs: + pr: ${{ steps.pr.outputs.pr }} + runs-on: ubuntu-22.04 + timeout-minutes: 1 + steps: + # Get PR number for squash merges to main + - name: PR Number + id: pr + uses: bcgov-nr/action-get-pr@v0.0.1 + deploys: name: PROD Deployments environment: prod @@ -18,35 +32,34 @@ jobs: include: - name: frontend file: frontend/openshift.nginx.deploy.yml - overwrite: true - name: rctool file: frontend/openshift.deploy.yml - overwrite: true steps: - uses: bcgov-nr/action-deployer-openshift@v2.1.0 with: file: ${{ matrix.file }} oc_namespace: ${{ secrets.OC_NAMESPACE }} - oc_server: ${{ secrets.OC_SERVER }} + oc_server: ${{ vars.OC_SERVER }} oc_token: ${{ secrets.OC_TOKEN }} - overwrite: ${{ matrix.overwrite }} + overwrite: true parameters: -p ZONE=prod -p PROMOTE=${{ github.repository }}/${{ matrix.name }}:test - -p NAME=${{ github.event.repository.name }} ${{ matrix.parameters }} - penetration_test: false image-promotions: - name: Promote images to PROD - needs: - - deploys + name: Promote Images + needs: [vars, deploys] + permissions: + packages: write runs-on: ubuntu-22.04 strategy: matrix: - component: [frontend, rctool] + package: [frontend, rctool] + timeout-minutes: 1 steps: - uses: shrink/actions-docker-registry-tag@v4 with: registry: ghcr.io - repository: ${{ github.repository }}/${{ matrix.component }} + repository: ${{ github.repository }}/${{ matrix.package }} target: test tags: prod + target: ${{ needs.vars.outputs.pr }} diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml new file mode 100644 index 00000000..25e937a4 --- /dev/null +++ b/.github/workflows/scheduled.yml @@ -0,0 +1,66 @@ +name: Scheduled + +on: + schedule: [cron: "0 11 * * 6"] # 3 AM PST = 12 PM UDT, Saturdays + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + ageOutPRs: + name: PR Env Purge + env: + # https://tecadmin.net/getting-yesterdays-date-in-bash/ + CUTOFF: "1 week ago" + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Clean up Helm Releases + run: | + oc login --token=${{ secrets.OC_TOKEN }} --server=${{ vars.OC_SERVER }} + oc project ${{ secrets.OC_NAMESPACE }} # Safeguard! + + # Catch errors, unset variables, and pipe failures (e.g. grep || true ) + set -euo pipefail + + # Echos + echo "Delete stale Helm releases" + echo "Cutoff: ${{ env.CUTOFF }}" + + # Before date, list of releases + BEFORE=$(date +%s -d "${{ env.CUTOFF }}") + RELEASES=$(helm ls -aq | grep ${{ github.event.repository.name }} || :) + + # If releases, then iterate + [ -z "${RELEASES}" ]|| for r in ${RELEASES[@]}; do + + # Get last update and convert the date + UPDATED=$(date "+%s" -d <<< echo $(helm status $r -o json | jq -r .info.last_deployed)) + + # Compare to cutoff and delete as necessary + if [[ ${UPDATED} < ${BEFORE} ]]; then + echo -e "\nOlder than cutoff: ${r}" + helm uninstall --no-hooks ${r} + oc delete pvc/${r}-bitnami-pg-0 + else + echo -e "\nNewer than cutoff: ${r}" + echo "No need to delete" + fi + done + + zap_scan: + runs-on: ubuntu-latest + name: Penetration Tests + env: + URL: ${{ github.event.repository.name }}-test-test-frontend.apps.silver.devops.gov.bc.ca/rctool/ + steps: + - name: ZAP Scan + uses: zaproxy/action-full-scan@v0.9.0 + with: + allow_issue_writing: true + artifact_name: "zap_rctool" + cmd_options: "-a" + issue_title: "ZAP: rctool" + target: https://${{ env.URL }} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml deleted file mode 100644 index 3793a9bc..00000000 --- a/.github/workflows/unit-tests.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Unit Tests and Analysis - -on: - pull_request: - types: - - opened - - reopened - - synchronize - - ready_for_review - push: - branches: - - main - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - # tests: - # name: Unit Tests - # if: github.event_name != 'pull_request' || !github.event.pull_request.draft - # runs-on: ubuntu-22.04 - # strategy: - # matrix: - # dir: [frontend] - # include: - # - dir: frontend - # sonar_projectKey: bcgov_nr-hydrometric-rating-curve_frontend - # token: SONAR_TOKEN_FRONTEND - # steps: - # - uses: bcgov-nr/action-test-and-analyse@v0.0.1 - # with: - # commands: | - # npm ci - # npm run test:cov - # dir: ${{ matrix.dir }} - # sonar_args: > - # -Dsonar.exclusions=**/coverage/**,**/node_modules/** - # -Dsonar.organization=bcgov-sonarcloud - # -Dsonar.project.monorepo.enabled=true - # -Dsonar.projectKey=${{ matrix.sonar_projectKey }} - # sonar_project_token: ${{ secrets[matrix.token] }} - - # https://github.com/marketplace/actions/aqua-security-trivy - trivy: - name: Trivy Security Scan - if: github.event_name != 'pull_request' || !github.event.pull_request.draft - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@0.18.0 - with: - format: "sarif" - output: "trivy-results.sarif" - ignore-unfixed: true - scan-type: "fs" - security-checks: "vuln,secret,config" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: "trivy-results.sarif"