@devopsplaybook.io/common-utils 1.0.0-beta.5.e74e0d9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.github/workflows/main-build.yml +17 -0
  2. package/.github/workflows/npm-upgrade.yml +16 -0
  3. package/.github/workflows/pr-check.yml +26 -0
  4. package/.github/workflows/reusable-merge-build.yml +137 -0
  5. package/.github/workflows/reusable-npm-merge.yml +131 -0
  6. package/.github/workflows/reusable-npm-pr.yml +149 -0
  7. package/.github/workflows/reusable-npm-upgrade.yml +92 -0
  8. package/.github/workflows/reusable-pr-verify.yml +131 -0
  9. package/README.md +1 -0
  10. package/dist/index.d.ts +8 -0
  11. package/dist/index.js +24 -0
  12. package/dist/src/ConfigBase.d.ts +104 -0
  13. package/dist/src/ConfigBase.js +201 -0
  14. package/dist/src/DbUtils.d.ts +50 -0
  15. package/dist/src/DbUtils.js +117 -0
  16. package/dist/src/DbUtilsNoTelemetry.d.ts +27 -0
  17. package/dist/src/DbUtilsNoTelemetry.js +89 -0
  18. package/dist/src/OTelContext.d.ts +35 -0
  19. package/dist/src/OTelContext.js +42 -0
  20. package/dist/src/PostgresDbUtils.d.ts +52 -0
  21. package/dist/src/PostgresDbUtils.js +217 -0
  22. package/dist/src/SqlDbUtils.d.ts +40 -0
  23. package/dist/src/SqlDbUtils.js +156 -0
  24. package/dist/src/SystemCommand.d.ts +9 -0
  25. package/dist/src/SystemCommand.js +56 -0
  26. package/dist/src/Timeout.d.ts +6 -0
  27. package/dist/src/Timeout.js +15 -0
  28. package/eslint.config.mjs +10 -0
  29. package/index.ts +8 -0
  30. package/jest.config.js +13 -0
  31. package/package.json +50 -0
  32. package/prettierrc.json +5 -0
  33. package/src/ConfigBase.spec.ts +108 -0
  34. package/src/ConfigBase.ts +213 -0
  35. package/src/DbUtils.spec.ts +23 -0
  36. package/src/DbUtils.ts +118 -0
  37. package/src/DbUtilsNoTelemetry.spec.ts +174 -0
  38. package/src/DbUtilsNoTelemetry.ts +121 -0
  39. package/src/OTelContext.spec.ts +58 -0
  40. package/src/OTelContext.ts +65 -0
  41. package/src/PostgresDbUtils.spec.ts +155 -0
  42. package/src/PostgresDbUtils.ts +233 -0
  43. package/src/SqlDbUtils.spec.ts +111 -0
  44. package/src/SqlDbUtils.ts +153 -0
  45. package/src/SystemCommand.spec.ts +18 -0
  46. package/src/SystemCommand.ts +23 -0
  47. package/src/Timeout.spec.ts +18 -0
  48. package/src/Timeout.ts +12 -0
  49. package/tsconfig.json +14 -0
  50. package/tsconfig.spec.json +7 -0
@@ -0,0 +1,17 @@
1
+ name: Main Build
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ npm-merge:
11
+ uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-merge.yml@main
12
+ with:
13
+ npm_package_name: "@devopsplaybook.io/common-utils"
14
+ secrets:
15
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
16
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
17
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
@@ -0,0 +1,16 @@
1
+ name: NPM Upgrade
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 6 * * 1"
6
+
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ npm-upgrade:
11
+ uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-upgrade.yml@main
12
+ permissions:
13
+ contents: write
14
+ pull-requests: write
15
+ with:
16
+ npm_services: "[]"
@@ -0,0 +1,26 @@
1
+ name: PR Check
2
+
3
+ on:
4
+ pull_request:
5
+ branches: ["main"]
6
+
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: pr-${{ github.event.pull_request.number || github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+ pull-requests: write
16
+ issues: write
17
+
18
+ jobs:
19
+ npm-pr:
20
+ uses: devopsplaybook-io/common-utils/.github/workflows/reusable-npm-pr.yml@main
21
+ with:
22
+ npm_package_name: "@devopsplaybook.io/common-utils"
23
+ secrets:
24
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
25
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
26
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
@@ -0,0 +1,137 @@
1
+ name: "Reusable: Merge Build"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ docker_platforms:
7
+ description: "Docker platforms to build for"
8
+ required: false
9
+ type: string
10
+ default: "linux/arm64/v8,linux/amd64"
11
+ node_app_directories:
12
+ description: "JSON array of Node.js app directories to build, lint, and test"
13
+ required: false
14
+ type: string
15
+ default: ""
16
+ node_version:
17
+ description: "Node.js version to use"
18
+ required: false
19
+ type: string
20
+ default: "22"
21
+ secrets:
22
+ DOCKER_HUB_USERNAME:
23
+ required: true
24
+ DOCKER_HUB_ACCESS_TOKEN:
25
+ required: true
26
+ QUALITY_DASHBOARD_TOKEN:
27
+ required: false
28
+ QUALITY_DASHBOARD_URL:
29
+ required: true
30
+
31
+ jobs:
32
+ node-build:
33
+ if: inputs.node_app_directories != ''
34
+ strategy:
35
+ matrix:
36
+ app: ${{ fromJSON(inputs.node_app_directories) }}
37
+ runs-on: ubuntu-latest
38
+ defaults:
39
+ run:
40
+ working-directory: ${{ matrix.app }}
41
+ steps:
42
+ - uses: actions/checkout@v6
43
+
44
+ - name: Setup Node.js
45
+ uses: actions/setup-node@v4
46
+ with:
47
+ node-version: ${{ inputs.node_version }}
48
+ cache: "npm"
49
+ cache-dependency-path: ${{ matrix.app }}/package-lock.json
50
+
51
+ - name: Install dependencies
52
+ run: npm ci
53
+
54
+ - name: Build
55
+ run: npm run build
56
+
57
+ - name: Lint
58
+ run: npm run lint
59
+
60
+ - name: Test
61
+ run: npm run test
62
+
63
+ - name: Zip coverage and upload to Quality Dashboard
64
+ if: always()
65
+ env:
66
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
67
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
68
+ run: |
69
+ APP_NAME="${{ matrix.app }}"
70
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
71
+ REPORT_KEY="$(echo "$APP_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')_coverage"
72
+
73
+ if [ -d coverage ]; then
74
+ # Zip coverage contents at root level so the jest processor
75
+ # finds coverage-final.json / clover.xml directly
76
+ (cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
77
+
78
+ META=$(jq -n \
79
+ --arg key "$REPORT_KEY" \
80
+ --arg pkg "$PACKAGE_NAME" \
81
+ '{
82
+ key: $key,
83
+ displayName: "Coverage: \($pkg)",
84
+ processor: "jest"
85
+ }')
86
+
87
+ echo "Uploading coverage: $REPORT_KEY"
88
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
89
+ -X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
90
+ -H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
91
+ -F "meta=$META" \
92
+ -F "file=@${{ runner.temp }}/coverage.zip" \
93
+ --max-time 30)
94
+
95
+ if [ "$HTTP_CODE" = "201" ]; then
96
+ echo "\u2713 Coverage uploaded ($HTTP_CODE)"
97
+ else
98
+ echo "\u26a0 Upload returned HTTP $HTTP_CODE"
99
+ fi
100
+ else
101
+ echo "No coverage/ directory found, skipping upload"
102
+ fi
103
+
104
+ docker-build:
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - uses: actions/checkout@v6
108
+
109
+ - name: Set up QEMU
110
+ uses: docker/setup-qemu-action@v4
111
+
112
+ - name: Set up Docker Buildx
113
+ uses: docker/setup-buildx-action@v4
114
+
115
+ - name: Login to Docker Hub
116
+ uses: docker/login-action@v4
117
+ with:
118
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
119
+ password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
120
+
121
+ - name: Build and Push Docker Image
122
+ run: |
123
+ set -e
124
+ SERVICE_NAME=$(cat package.json | jq -r '.name')
125
+ SERVICE_VERSION=$(cat package.json | jq -r '.version')
126
+ SERVICE_VERSION_MAJOR=$(echo "${SERVICE_VERSION}" | cut -f1 -d".")
127
+ SERVICE_VERSION_MINOR=$(echo "${SERVICE_VERSION}" | cut -f1-2 -d".")
128
+ echo "Building ${SERVICE_NAME}:${SERVICE_VERSION}"
129
+ docker buildx build \
130
+ --platform ${{ inputs.docker_platforms }} \
131
+ --push \
132
+ -f Dockerfile \
133
+ -t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:latest \
134
+ -t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:${SERVICE_VERSION} \
135
+ -t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:${SERVICE_VERSION_MAJOR} \
136
+ -t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:${SERVICE_VERSION_MINOR} \
137
+ .
@@ -0,0 +1,131 @@
1
+ name: "Reusable: NPM Merge"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ node_version:
7
+ description: "Node.js version to use"
8
+ required: false
9
+ type: string
10
+ default: "18"
11
+ npm_package_name:
12
+ description: "NPM package name (e.g. @devopsplaybook.io/otel-utils)"
13
+ required: true
14
+ type: string
15
+ secrets:
16
+ NPM_TOKEN:
17
+ required: true
18
+ QUALITY_DASHBOARD_TOKEN:
19
+ required: false
20
+ QUALITY_DASHBOARD_URL:
21
+ required: true
22
+
23
+ jobs:
24
+ test-and-build:
25
+ runs-on: ubuntu-latest
26
+
27
+ steps:
28
+ - uses: actions/checkout@v6
29
+
30
+ - name: Setup Node.js
31
+ uses: actions/setup-node@v4
32
+ with:
33
+ node-version: ${{ inputs.node_version }}
34
+ cache: "npm"
35
+
36
+ - name: Install dependencies
37
+ run: npm ci
38
+
39
+ - name: Run linting
40
+ run: npm run lint
41
+
42
+ - name: Run tests
43
+ run: npm test
44
+
45
+ - name: Zip coverage and upload to Quality Dashboard
46
+ if: always()
47
+ env:
48
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
49
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
50
+ run: |
51
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
52
+ REPORT_KEY=$(echo "$PACKAGE_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')
53
+ REPORT_KEY="${REPORT_KEY}_coverage"
54
+
55
+ if [ -d coverage ]; then
56
+ # Zip coverage contents at root level so the jest processor
57
+ # finds coverage-final.json / clover.xml directly
58
+ (cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
59
+
60
+ META=$(jq -n \
61
+ --arg key "$REPORT_KEY" \
62
+ --arg pkg "$PACKAGE_NAME" \
63
+ '{
64
+ key: $key,
65
+ displayName: "Coverage: \($pkg)",
66
+ processor: "jest"
67
+ }')
68
+
69
+ echo "Uploading coverage: $REPORT_KEY"
70
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
71
+ -X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
72
+ -H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
73
+ -F "meta=$META" \
74
+ -F "file=@${{ runner.temp }}/coverage.zip" \
75
+ --max-time 30)
76
+
77
+ if [ "$HTTP_CODE" = "201" ]; then
78
+ echo "\u2713 Coverage uploaded ($HTTP_CODE)"
79
+ else
80
+ echo "\u26a0 Upload returned HTTP $HTTP_CODE"
81
+ fi
82
+ else
83
+ echo "No coverage/ directory found, skipping upload"
84
+ fi
85
+
86
+ - name: Build package
87
+ run: npm run build
88
+
89
+ publish-release:
90
+ runs-on: ubuntu-latest
91
+ needs: test-and-build
92
+
93
+ steps:
94
+ - uses: actions/checkout@v6
95
+ with:
96
+ fetch-depth: 0
97
+
98
+ - name: Setup Node.js
99
+ uses: actions/setup-node@v4
100
+ with:
101
+ node-version: ${{ inputs.node_version }}
102
+ cache: "npm"
103
+ registry-url: "https://registry.npmjs.org"
104
+
105
+ - name: Install dependencies
106
+ run: npm ci
107
+
108
+ - name: Build package
109
+ run: npm run build
110
+
111
+ - name: Check if version changed
112
+ id: version-check
113
+ run: |
114
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
115
+ echo "current-version=${CURRENT_VERSION}" >> $GITHUB_OUTPUT
116
+
117
+ if npm view ${{ inputs.npm_package_name }}@${CURRENT_VERSION} version &> /dev/null; then
118
+ echo "version-exists=true" >> $GITHUB_OUTPUT
119
+ echo "Version ${CURRENT_VERSION} already exists on npm"
120
+ else
121
+ echo "version-exists=false" >> $GITHUB_OUTPUT
122
+ echo "Version ${CURRENT_VERSION} is new, will publish"
123
+ fi
124
+
125
+ - name: Publish release package
126
+ if: steps.version-check.outputs.version-exists == 'false'
127
+ run: |
128
+ echo "Publishing version ${{ steps.version-check.outputs.current-version }}"
129
+ npm publish
130
+ env:
131
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,149 @@
1
+ name: "Reusable: NPM PR"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ node_version:
7
+ description: "Node.js version to use"
8
+ required: false
9
+ type: string
10
+ default: "18"
11
+ npm_package_name:
12
+ description: "NPM package name (e.g. @devopsplaybook.io/otel-utils)"
13
+ required: true
14
+ type: string
15
+ secrets:
16
+ NPM_TOKEN:
17
+ required: true
18
+ QUALITY_DASHBOARD_TOKEN:
19
+ required: false
20
+ QUALITY_DASHBOARD_URL:
21
+ required: true
22
+
23
+ jobs:
24
+ test-and-build:
25
+ runs-on: ubuntu-latest
26
+
27
+ steps:
28
+ - uses: actions/checkout@v6
29
+
30
+ - name: Setup Node.js
31
+ uses: actions/setup-node@v4
32
+ with:
33
+ node-version: ${{ inputs.node_version }}
34
+ cache: "npm"
35
+
36
+ - name: Install dependencies
37
+ run: npm ci
38
+
39
+ - name: Run linting
40
+ run: npm run lint
41
+
42
+ - name: Run tests
43
+ run: npm test
44
+
45
+ - name: Zip coverage and upload to Quality Dashboard
46
+ if: always()
47
+ env:
48
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
49
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
50
+ run: |
51
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
52
+ REPORT_KEY=$(echo "$PACKAGE_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')
53
+ REPORT_KEY="${REPORT_KEY}_coverage"
54
+
55
+ if [ -d coverage ]; then
56
+ # Zip coverage contents at root level so the jest processor
57
+ # finds coverage-final.json / clover.xml directly
58
+ (cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
59
+
60
+ META=$(jq -n \
61
+ --arg key "$REPORT_KEY" \
62
+ --arg pkg "$PACKAGE_NAME" \
63
+ '{
64
+ key: $key,
65
+ displayName: "Coverage: \($pkg)",
66
+ processor: "jest"
67
+ }')
68
+
69
+ echo "Uploading coverage: $REPORT_KEY"
70
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
71
+ -X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
72
+ -H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
73
+ -F "meta=$META" \
74
+ -F "file=@${{ runner.temp }}/coverage.zip" \
75
+ --max-time 30)
76
+
77
+ if [ "$HTTP_CODE" = "201" ]; then
78
+ echo "\u2713 Coverage uploaded ($HTTP_CODE)"
79
+ else
80
+ echo "\u26a0 Upload returned HTTP $HTTP_CODE"
81
+ fi
82
+ else
83
+ echo "No coverage/ directory found, skipping upload"
84
+ fi
85
+
86
+ - name: Build package
87
+ run: npm run build
88
+
89
+ publish-beta:
90
+ runs-on: ubuntu-latest
91
+ needs: test-and-build
92
+ if: github.event_name == 'pull_request'
93
+
94
+ steps:
95
+ - uses: actions/checkout@v6
96
+
97
+ - name: Setup Node.js
98
+ uses: actions/setup-node@v4
99
+ with:
100
+ node-version: ${{ inputs.node_version }}
101
+ cache: "npm"
102
+ registry-url: "https://registry.npmjs.org"
103
+
104
+ - name: Install dependencies
105
+ run: npm ci
106
+
107
+ - name: Build package
108
+ run: npm run build
109
+
110
+ - name: Generate beta version
111
+ id: beta-version
112
+ run: |
113
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
114
+ PR_NUMBER=${{ github.event.pull_request.number }}
115
+ COMMIT_SHA=${GITHUB_SHA:0:7}
116
+ BETA_VERSION="${CURRENT_VERSION}-beta.${PR_NUMBER}.${COMMIT_SHA}"
117
+ echo "version=${BETA_VERSION}" >> $GITHUB_OUTPUT
118
+ echo "Beta version will be: ${BETA_VERSION}"
119
+
120
+ - name: Update package.json with beta version
121
+ run: |
122
+ npm version ${{ steps.beta-version.outputs.version }} --no-git-tag-version
123
+
124
+ - name: Publish beta package
125
+ run: npm publish --tag beta
126
+ env:
127
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
128
+
129
+ - name: Comment PR with beta version info
130
+ uses: actions/github-script@v7
131
+ with:
132
+ script: |
133
+ github.rest.issues.createComment({
134
+ issue_number: context.issue.number,
135
+ owner: context.repo.owner,
136
+ repo: context.repo.repo,
137
+ body: `🚀 **Beta version published!**
138
+
139
+ **Package**: \`${{ inputs.npm_package_name }}@${{ steps.beta-version.outputs.version }}\`
140
+
141
+ Install with:
142
+ \`\`\`bash
143
+ npm install ${{ inputs.npm_package_name }}@${{ steps.beta-version.outputs.version }}
144
+ # or
145
+ npm install ${{ inputs.npm_package_name }}@beta
146
+ \`\`\`
147
+
148
+ This beta version will be available for testing until the PR is merged.`
149
+ })
@@ -0,0 +1,92 @@
1
+ name: "Reusable: NPM Upgrade"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ npm_services:
7
+ description: "JSON array of sub-folders containing NPM services to update"
8
+ required: true
9
+ type: string
10
+ pr_branch:
11
+ description: "Branch name for the upgrade PR (defaults to feature/YYYY.MM.DD-dependency-updates)"
12
+ required: false
13
+ type: string
14
+ default: ""
15
+
16
+ jobs:
17
+ npm-upgrade:
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ contents: write
21
+ pull-requests: write
22
+ steps:
23
+ - uses: actions/checkout@v6
24
+
25
+ - name: Resolve branch name
26
+ id: branch
27
+ run: |
28
+ if [ -n "${{ inputs.pr_branch }}" ]; then
29
+ echo "name=${{ inputs.pr_branch }}" >> $GITHUB_OUTPUT
30
+ else
31
+ echo "name=feature/$(date +'%Y.%m.%d')-dependency-updates" >> $GITHUB_OUTPUT
32
+ fi
33
+
34
+ - name: Check for existing PR
35
+ id: check_pr
36
+ env:
37
+ GH_TOKEN: ${{ github.token }}
38
+ run: |
39
+ EXISTING_PR=$(gh pr list --state open --json number --jq 'length')
40
+ if [ "${EXISTING_PR}" -gt 0 ]; then
41
+ echo "An open PR already exists, skipping"
42
+ echo "skip=true" >> $GITHUB_OUTPUT
43
+ else
44
+ echo "skip=false" >> $GITHUB_OUTPUT
45
+ fi
46
+
47
+ - name: Update NPM dependencies
48
+ if: steps.check_pr.outputs.skip == 'false'
49
+ run: |
50
+ set -e
51
+ # Update root package
52
+ echo "Updating dependencies in root"
53
+ npx npm-check-updates -u
54
+ npm install
55
+ # Update sub-service packages
56
+ SERVICES='${{ inputs.npm_services }}'
57
+ for FOLDER in $(echo "${SERVICES}" | jq -r '.[]'); do
58
+ echo "Updating dependencies in ${FOLDER}"
59
+ cd "${FOLDER}"
60
+ npx npm-check-updates -u
61
+ npm install
62
+ cd "${GITHUB_WORKSPACE}"
63
+ done
64
+ if [ -n "$(git status --porcelain)" ]; then
65
+ echo "HAS_CHANGES=true" >> $GITHUB_ENV
66
+ fi
67
+
68
+ - name: Bump root package.json patch version
69
+ if: steps.check_pr.outputs.skip == 'false' && env.HAS_CHANGES == 'true'
70
+ run: |
71
+ set -e
72
+ CURRENT_VERSION=$(jq -r '.version' package.json)
73
+ NEW_VERSION=$(echo "${CURRENT_VERSION}" | awk -F. '{print $1"."$2"."$3+1}')
74
+ jq --arg v "${NEW_VERSION}" '.version = $v' package.json > package.json.tmp && mv package.json.tmp package.json
75
+ echo "Bumped root package.json version: ${CURRENT_VERSION} -> ${NEW_VERSION}"
76
+
77
+ - name: Create Pull Request
78
+ if: steps.check_pr.outputs.skip == 'false' && env.HAS_CHANGES == 'true'
79
+ env:
80
+ GH_TOKEN: ${{ github.token }}
81
+ run: |
82
+ git config user.name "github-actions[bot]"
83
+ git config user.email "github-actions[bot]@users.noreply.github.com"
84
+ git checkout -b "${{ steps.branch.outputs.name }}"
85
+ git add -A
86
+ git commit -m "chore: update NPM dependencies"
87
+ git push origin "${{ steps.branch.outputs.name }}"
88
+ gh pr create \
89
+ --title "chore: update NPM dependencies" \
90
+ --body "Automated NPM dependency update" \
91
+ --head "${{ steps.branch.outputs.name }}" \
92
+ --base main
@@ -0,0 +1,131 @@
1
+ name: "Reusable: PR Verify"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ docker_platforms:
7
+ description: "Docker platforms to build for"
8
+ required: false
9
+ type: string
10
+ default: "linux/arm64/v8,linux/amd64"
11
+ node_app_directories:
12
+ description: "JSON array of Node.js app directories to build, lint, and test"
13
+ required: false
14
+ type: string
15
+ default: ""
16
+ node_version:
17
+ description: "Node.js version to use"
18
+ required: false
19
+ type: string
20
+ default: "22"
21
+ secrets:
22
+ DOCKER_HUB_USERNAME:
23
+ required: true
24
+ DOCKER_HUB_ACCESS_TOKEN:
25
+ required: true
26
+ QUALITY_DASHBOARD_TOKEN:
27
+ required: false
28
+ QUALITY_DASHBOARD_URL:
29
+ required: true
30
+
31
+ jobs:
32
+ node-build:
33
+ if: inputs.node_app_directories != ''
34
+ strategy:
35
+ matrix:
36
+ app: ${{ fromJSON(inputs.node_app_directories) }}
37
+ runs-on: ubuntu-latest
38
+ defaults:
39
+ run:
40
+ working-directory: ${{ matrix.app }}
41
+ steps:
42
+ - uses: actions/checkout@v6
43
+
44
+ - name: Setup Node.js
45
+ uses: actions/setup-node@v4
46
+ with:
47
+ node-version: ${{ inputs.node_version }}
48
+ cache: "npm"
49
+ cache-dependency-path: ${{ matrix.app }}/package-lock.json
50
+
51
+ - name: Install dependencies
52
+ run: npm ci
53
+
54
+ - name: Build
55
+ run: npm run build
56
+
57
+ - name: Lint
58
+ run: npm run lint
59
+
60
+ - name: Test
61
+ run: npm run test
62
+
63
+ - name: Zip coverage and upload to Quality Dashboard
64
+ if: always()
65
+ env:
66
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
67
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
68
+ run: |
69
+ APP_NAME="${{ matrix.app }}"
70
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
71
+ REPORT_KEY="$(echo "$APP_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')_coverage"
72
+
73
+ if [ -d coverage ]; then
74
+ # Zip coverage contents at root level so the jest processor
75
+ # finds coverage-final.json / clover.xml directly
76
+ (cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
77
+
78
+ META=$(jq -n \
79
+ --arg key "$REPORT_KEY" \
80
+ --arg pkg "$PACKAGE_NAME" \
81
+ '{
82
+ key: $key,
83
+ displayName: "Coverage: \($pkg)",
84
+ processor: "jest"
85
+ }')
86
+
87
+ echo "Uploading coverage: $REPORT_KEY"
88
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
89
+ -X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
90
+ -H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
91
+ -F "meta=$META" \
92
+ -F "file=@${{ runner.temp }}/coverage.zip" \
93
+ --max-time 30)
94
+
95
+ if [ "$HTTP_CODE" = "201" ]; then
96
+ echo "\u2713 Coverage uploaded ($HTTP_CODE)"
97
+ else
98
+ echo "\u26a0 Upload returned HTTP $HTTP_CODE"
99
+ fi
100
+ else
101
+ echo "No coverage/ directory found, skipping upload"
102
+ fi
103
+
104
+ docker-build:
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - uses: actions/checkout@v6
108
+
109
+ - name: Set up QEMU
110
+ uses: docker/setup-qemu-action@v4
111
+
112
+ - name: Set up Docker Buildx
113
+ uses: docker/setup-buildx-action@v4
114
+
115
+ - name: Login to Docker Hub
116
+ uses: docker/login-action@v4
117
+ with:
118
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
119
+ password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
120
+
121
+ - name: Build and Push Docker Image
122
+ run: |
123
+ set -e
124
+ SERVICE_NAME=$(cat package.json | jq -r '.name')
125
+ echo "Building ${SERVICE_NAME}:beta"
126
+ docker buildx build \
127
+ --platform ${{ inputs.docker_platforms }} \
128
+ --push \
129
+ -f Dockerfile \
130
+ -t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:beta \
131
+ .