diff --git a/.forgejo/workflows/stagging.yml b/.forgejo/workflows/stagging.yml new file mode 100644 index 0000000..adc0574 --- /dev/null +++ b/.forgejo/workflows/stagging.yml @@ -0,0 +1,237 @@ +# +=========================================================================================================================+ +# | QA/Stagging CI/CD workflow | +# | | +# | Our QA/Stagging environment is hosted on servers in our office using a combination of: | +# | - Kubernetes (10 Ubuntu 24.04 VM nodes, hosted within a Proxmox VE cluster of 2 servers, a Dell R730xd and a Dell R730) | +# | - Nginx Proxy Manager as a reverse proxy | +# | - Pi-hole for DHCP | +# | - Bind for custom DNS | +# | On a cluster wide level: | +# | - Rook/Ceph | +# | - MetalLb | +# | - Nginx Ingress Controller | +# | - PosgreSQL Operator (CloudNativePG) | +# | - Custom PostgreSQL Database Operation (https://github.com/AlanBridgeman/postgres-controller) | +# | - MongoDB Community Operator | +# | On a namespace level: | +# | - Redis | +# | - Hasshicorp Vault | +# | - Mongo containers | +# | | +# | It's worth noting we largely use Helm to deploy resources into the cluster. | +# +=========================================================================================================================+ + +name: Build and deploy Main Bridgeman Accessible website to QA/Stagging (private Kubernetes cluster) + +on: + push: + branches: + - stagging + # I'm leaving this here because I've tried to add and then had to remove this several times + # In particular, the problem is conditions are OR'd NOT AND'd + # This means controls via branch commit rules combined with this workflow achieve a better result than having this here + #tags: + # - 'v[0-9]+.[0-9]+.[0-9]+' # Matches any semantic versioning tag (e.g. v1.0.0, v2.1.3, etc.) + workflow_dispatch: + +permissions: + contents: write # Allows the workflow to push changes to the repository (e.g. for tagging) + +jobs: + # Run tests (particularly unit tests) on the codebase + # This mostly relies on the `yarn test` command + #test: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v4 + # + # - name: Set up Node.js version + # uses: actions/setup-node@v3 + # with: + # # Take from [Repo README](https://github.com/actions/setup-node#readme) + # # + # # > Version Spec of the version to use in SemVer notation. + # # > It also admits such aliases as lts/*, latest, nightly and canary builds + # # > Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node + # node-version: '18.x' + # + # # Take from [Repo README](https://github.com/actions/setup-node#readme) + # # + # # > Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, + # # > and set up auth to read in from env.NODE_AUTH_TOKEN. + # # > Default: '' + # registry-url: 'https://npm.pkg.bridgemanaccessible.ca' + # + # # Take from [Repo README](https://github.com/actions/setup-node#readme) + # # + # # > Optional scope for authenticating against scoped registries. + # # > Will fall back to the repository owner when using the GitHub Packages registry (https://npm.pkg.github.com/). + # scope: '@BridgemanAccessible' + # + # - name: yarn install and test + # env: + # NODE_AUTH_TOKEN: ${{ secrets.PRIVATE_NPM_TOKEN }} + # run: | + # yarn install + # yarn test + + # Builds the codebase (builds the image) + build: + runs-on: self-hosted + outputs: + build_and_deploy: ${{ steps.version_check.outputs.build_and_deploy }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Prepare Environment Variables + run: | + echo "Harbor Username: ${{ secrets.HARBOR_USERNAME }}" + echo "HARBOR_USERNAME=${{ secrets.HARBOR_USERNAME }}" >> $GITHUB_ENV + echo "Harbor Password: ${{ secrets.HARBOR_PASSWORD }}" + echo "HARBOR_PASSWORD=${{ secrets.HARBOR_PASSWORD }}" >> $GITHUB_ENV + + # Check if this is a "significant" commit that should trigger a build and deploy + # This is done by checking if the latest git tag matches the version in the `package.json` file + - name: Version Check + id: version_check + run: | + echo 'Harbor Username: ${{ env.HARBOR_USERNAME }}' + + # Get the latest version from the `package.json` file + # This should be updated if a new build and deploy is desired + LATEST_VERSION=$(jq -r '.version' package.json) + echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_ENV + + # Get the latest Git tag from the repository + LATEST_TAG=$(git describe --tags --abbrev=0) + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + + # If the git tag and the version ARE the same, this is a commit that SHOULD NOT trigger a build and deploy + # Inversely, if the git tag and the version are NOT the same, this is a commit that SHOULD trigger a build and deploy + if [[ "$LATEST_TAG" == "v$LATEST_VERSION" ]]; then + echo "Latest tag matches the version in package.json, SHOULD NOT build and deploy" + echo "build_and_deploy=false" >> $GITHUB_OUTPUT + else + echo "Latest tag does not match the version in package.json, SHOULD build and deploy" + echo "build_and_deploy=true" >> $GITHUB_OUTPUT + fi + + # Because a build and deploy is desired, we want to check if a new tag already exists or if we need to create one as part of the workflow + # This is commented out because the approach here makes sense but the logic doesn't fully because we already know the git tag and the version don't match + #- name: Tag Check + # id: tag_check + # if: steps.version_check.outputs.build_and_deploy == 'true' + # run: | + # # Check if a matching image build already exists + # MATCHING_IMAGE=$(docker image ls $(jq -r '.name' package.json) | grep ${{ env.LATEST_TAG }} || echo "Image not found") + # + # # If a matching image exists, set the `create_tag` output to true (so that a new tag is created) + # if [[ "$MATCHING_IMAGE" == "Image not found" ]]; then + # echo "No matching image found, No new tag required" + # echo "create_tag=false" >> $GITHUB_OUTPUT + # else + # echo "Matching image found, new tag required" + # echo "create_tag=true" >> $GITHUB_OUTPUT + # fi + + # Conditional step to create a new Git tag + # This is required because when we go to build and deploy the image we rely on the latest git tag + # We do it this way so that ONLY situations where the git tag and the last built image are mal-aligned do we create a new tag + - name: Add Git Tag (if needed) + if: steps.version_check.outputs.build_and_deploy == 'true' + #if: steps.version_check.outputs.build_and_deploy && steps.tag_check.outputs.create_tag == 'true' + run: | + # Increment the version's patch number (within the `package.json` file) + #jq '.version |= (. | split(".") | [.[0], .[1], (.[2] | tonumber + 1 | tostring)] | join("."))' package.json > temp.json && mv temp.json package.json + + # Update remote URL to use the GITHUB_TOKEN for authentication + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@git.bridgemanaccessible.ca/${{ github.repository }}.git + + # Setup git user details for committing the version change and tag + git config user.name "Forgjo Actions" + git config user.email "actions@git.bridgemanaccessible.ca" + + # Commit the version change to the `package.json` file + #git add package.json + #git commit -m "[Github Actions] Update version to $(jq -r '.version' package.json)" + + # Push the changes to the repository + #git push origin HEAD:stagging + + # Create a new tag with the updated version number + git tag -a "v${{ env.LATEST_VERSION }}" -m "Version ${{ env.LATEST_VERSION }}" + + # Push the new tag to the repository + git push --tags + + # Run the build script found on self-hosted runner + - name: Run Build Image Script + if: steps.version_check.outputs.build_and_deploy == 'true' + run: | + IMAGE_NAME=ba-website + + echo "Repository Path: $GITHUB_WORKSPACE" + echo "Name for Image: $IMAGE_NAME" + + # Not necessarily needed but keeping track of the current directory (so that we can return to it is just a good practice) + CURR_DIR="$PWD" + + # Change into the repository directory + cd "$GITHUB_WORKSPACE" + + IMAGE_TAG=$(git describe --tags --abbrev=0) # Use the most recent tag as the image tag + IMAGE_FULL_NAME="containers.bridgemanaccessible.ca/k8s/${IMAGE_NAME}:${IMAGE_TAG}" + + echo "🔹 Building and pushing image..." + docker build -t ${IMAGE_FULL_NAME} . + docker login -u $HARBOR_USERNAME -p $HARBOR_PASSWORD containers.bridgemanaccessible.ca + docker push ${IMAGE_FULL_NAME} + + cd "$CURR_DIR" + + echo "✅ Build successful!" + + deploy: + runs-on: self-hosted + needs: build + if: needs.build.outputs.build_and_deploy == 'true' + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get the latest tag + id: get_latest_tag + run: | + LATEST_TAG=$(git describe --tags --abbrev=0) + echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV + echo "IMAGE_VERSION=${LATEST_TAG#v}" >> $GITHUB_ENV + + #- name: Prepare Environment Variables + # run: | + # echo "HARBOR_USERNAME=${{ secrets.HARBOR_USERNAME }}" >> $GITHUB_ENV + # echo "HARBOR_PASSWORD=${{ secrets.HARBOR_PASSWORD }}" >> $GITHUB_ENV + + - name: Run Deploy Kubernetes Script + run: | + IMAGE_VERSION="${{ env.IMAGE_VERSION }}" + NAMESPACE="ba-website" + DEPLOYMENT_NAME="website" + CONTAINER_NAME="website" + IMAGE_NAME="ba-website" + + echo "Image Version: $IMAGE_VERSION" + echo "Namespace: $NAMESPACE" + echo "Deployment Name: $DEPLOYMENT_NAME" + echo "Container Name: $CONTAINER_NAME" + echo "Image Name: $IMAGE_NAME" + + update-k8s-deployment-image \ + --image-version $IMAGE_VERSION \ + --namespace $NAMESPACE \ + --deployment-name $DEPLOYMENT_NAME \ + --container-name $CONTAINER_NAME \ + --image-name "containers.bridgemanaccessible.ca/k8s/$IMAGE_NAME" \ No newline at end of file diff --git a/package.json b/package.json index cff249c..7c5c066 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ba-website", - "version": "1.0.0", - "description": "A simple Express app", + "version": "1.0.16", + "description": "The Bridgeman Accessible main website", "main": "server.js", "scripts": { "start": "node dist/server.js",