I ended up creating a GitHub Action to deploy to o...
# dev-ops
d
I ended up creating a GitHub Action to deploy to our staging & production environments. Because GitHub Actions allow for a manual approval step, we could pause after deploying to the staging environment to wait for QA approval before deploying to production. And this lets us also manually release to production so that we could coordinate timing. When the Action runs, it'll generate a
deploy.xml
based on the files changed in the feature branch. An Action would then deploy to staging. QA would test and validate. Then on release day, a release manager would then approve the Action(s) to deploy to production. Or if a feature hadn't completed QA then that Action would stay in the manual approval state. Our process flow went as: • developer checks out
main
into a
feature
branch. • they would manually deploy to their sandbox or shared development account. • they would create a pull request to
main
, which would run various lints, tests, analysis, etc. and require approval. • when the pull request was merged into
main
, then the Deployment Action would start: ◦ it'd create a
deploy.xml
and deploy to the staging environment. ◦ wait for manual approval ◦ then finally deploy to the production environment • I had an extra step to cherry-pick the merge commit into a
release
branch, and this would maintain a linear history of production deployments (because the GitHub Action to deploy to production could happen out of order) This flow isn't perfect, but it was pretty good for automating the parts that we needed, with the limitations we had. (For instance, sometimes a developer might release a feature to QA, but it might be several sprints before QA validates it, or they might be blocked for a few sprints) Why not deploy to QA as a part of the pull request? Because if there are 2 developers working on the same file in different feature branches, both with open pull requests, then that file would be overwritten in the staging environment when the 2nd pull request action runs. The real pain in the butt happens when a bug is found by QA in the staging environment. Because the pull request has already been closed, and the code merged into
main
, there's the possibility the other developers have checked out from
main
which now also include the bug. Which now involves reverting the pull request and new pull requests will need to sync again.
m
That sounds very cool. I'd be interested in seeing more about your github action if that is something you're willing to share.
d
yeah, I'll gather some of my notes to share tomorrow, my company doesn't exist anymore and has dropped NetSuite
Hey @Mike Robbins, oh I should add that we only ever got around to implementing this for js files only. We never got around to setting this up for custom records or anything. I'm using bash to run some git commands to get a list of modified files from the feature branch, then output that into a deploy.xml file.
Copy code
name: NetSuite Deployment

on: 
  push:
    branches:
      - main
    paths:
      - src/**
  workflow_dispatch:

jobs:

  generate-changeset:
    runs-on: ubuntu-latest
    steps:
    - name: Check out code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Generate Changeset XML
      run: |
        # get just the file names, of changed (added or modified, but not deleted) files in the src folder
        #
        git_files=$(git diff --no-commit-id --name-only -r --diff-filter=d ${{ github.event.before }} "$GITHUB_SHA" -- src)
        echo $git_files

        if [ -z "$git_files" ]; then
          echo "No src/ files changed"
          exit 1
        fi

        # get just a list of .js files
        #
        git_files_js=$(grep '\.js$' <<< $git_files)
        echo $git_files_js
        
        # build a deploy.xml using the list
        #
        echo "<deploy>" > src/deploy.xml
        echo "  <files>" >> src/deploy.xml
        while IFS= read -r file; do
          echo "    <path>${file}</path>" | sed 's/src/~/' >> src/deploy.xml
        done <<< $git_files_js
        echo "  </files>" >> src/deploy.xml
        echo "</deploy>" >> src/deploy.xml

        cat src/deploy.xml

      shell: bash

    - name: Archive Changeset XML
      uses: actions/upload-artifact@v4
      with:
        name: deploy-xml
        path: 
          src/deploy.xml

  deploy-staging:
    needs: generate-changeset
    uses: ./.github/workflows/suitecloud-deploy.yaml
    with:
      target-env: staging
    secrets: inherit

  deploy-production:
    needs: deploy-staging
    uses: ./.github/workflows/suitecloud-deploy.yaml
    with:
      target-env: production
    secrets: inherit

  merge-into-release-branch:
    needs: deploy-production
    uses: ./.github/workflows/release-branch.yaml
    with:
      GH_SHA: ${{ github.sha}}
I re-use suitecloud-deploy.yaml a few times. I have some notes in here about configuring the github action runner to use the already installed JRE 17.
Copy code
name: SuiteCloud Deployment

on: 
  workflow_call:
    inputs:
      target-env:
        required: true
        type: string
    secrets:
      TBA_TOKEN_KEY:
        required: true
      TBA_TOKEN_SECRET:
        required: true

jobs:
  deploy:
    name: Deploy to ${{ inputs.target-env }}
    runs-on: ubuntu-latest
    environment: ${{ inputs.target-env}}

    steps:
      
      - name: checkout
        uses: actions/checkout@v4

      - name: download artifacts
        uses: actions/download-artifact@v4
        with:
          name: deploy-xml
          path: src

      - name: deploy
        run: |
          pwd
          ls src
          cat src/deploy.xml
          echo "We need to 
           * install npm
           * install oracle java jre 17 (we don't need the JDK, just JRE 17 which is already installed in the github runner)
           * install suitecloud-cli
           * package/discover scripts to deploy
           * deploy to the ${{ inputs.target-env }} environment"

      - name: Set JAVA_HOME for Java 17
        run: |
          # The GitHub Runner has a bunch of JRE's installed, set the appropriate JRE by setting
          # JAVA_HOME, and persisting it to the environment variable by setting GITHUB_ENV
          # <https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md#java> 
          #
          echo "set JAVA_HOME so that it uses the runners JRE 17"
          export JAVA_HOME=$JAVA_HOME_17_X64
          export PATH=$JAVA_HOME/bin:$PATH
          echo "JAVA_HOME=$JAVA_HOME_17_X64" >> $GITHUB_ENV
          echo "PATH=$JAVA_HOME/bin:$PATH" >> $GITHUB_ENV


      - name: Setup Node
        uses: actions/setup-node@v4
        with: 
          node-version: '18.14.2' 

      - name: Install SuiteCloud
        run: npm install -g --acceptSuiteCloudSDKLicense @oracle/suitecloud-cli

      - name: Fetch NetSuite Token
        run: suitecloud account:savetoken --authid ${{ inputs.target-env}}-${{ vars.ACCOUNT_ID }} --account ${{ vars.ACCOUNT_ID }} --tokenid ${{ secrets.TBA_TOKEN_KEY }} --tokensecret ${{ secrets.TBA_TOKEN_SECRET }}

      - name: run deployment
        run: |
          suitecloud project:validate --server 
          suitecloud project:adddependencies
          suitecloud project:deploy
in my GitHub repository, I've setup two environments: • staging • production the production environment has deployment protection rules setup, so that approval is required before that deployment can run. Same with secrets, those are stored in each github environment, to keep keys separated.
hope this helps someone
m
Thanks for that writeup. I appreciate it!
s
@dbaghdanov how long did it take to run these actions - i.e. time for the overhead of installing npm/suitecloud/setup vs. the actual
suitecloud project:deploy
command itself?