diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9509dc..0a77c81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,10 +17,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Use Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' @@ -57,12 +57,12 @@ jobs: PGPASSWORD: postgres PGDATABASE: postgres steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Use Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' @@ -94,7 +94,7 @@ jobs: run: npm run test -- --coverage - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 - name: Print integration environment logs run: cat docker-compose-logs.txt @@ -107,6 +107,7 @@ jobs: build-publish: permissions: contents: write + id-token: write issues: write pull-requests: write runs-on: ubuntu-latest @@ -116,16 +117,15 @@ jobs: steps: - name: Generate release bot app token id: generate_token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ secrets.HIROSYSTEMS_RELEASE_BOT_ID }} private-key: ${{ secrets.HIROSYSTEMS_RELEASE_BOT_PEM }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 - persist-credentials: false + - name: Get bot user ID id: bot-user-id run: | @@ -133,9 +133,10 @@ jobs: env: GH_TOKEN: ${{ steps.generate_token.outputs.token }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' + registry-url: 'https://registry.npmjs.org' - name: Install deps run: npm ci --audit=false @@ -144,18 +145,16 @@ jobs: run: npm run build - name: Semantic Release - uses: cycjimmy/semantic-release-action@v4 + uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6 # Only run on non-PR events or only PRs that aren't from forks if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} SEMANTIC_RELEASE_PACKAGE: ${{ github.event.repository.name }} GIT_AUTHOR_EMAIL: "${{ steps.bot-user-id.outputs.user-id }}+${{ steps.generate_token.outputs.app-slug }}[bot]@users.noreply.github.com" GIT_COMMITTER_EMAIL: "${{ steps.bot-user-id.outputs.user-id }}+${{ steps.generate_token.outputs.app-slug }}[bot]@users.noreply.github.com" with: - semantic_version: 19 extra_plugins: | @semantic-release/changelog@6.0.3 @semantic-release/git@10.0.1 - conventional-changelog-conventionalcommits@6.1.0 + conventional-changelog-conventionalcommits@9.1.0 diff --git a/src/postgres/__tests__/base-pg-store.test.ts b/src/postgres/__tests__/base-pg-store.test.ts index aa92041..694440a 100644 --- a/src/postgres/__tests__/base-pg-store.test.ts +++ b/src/postgres/__tests__/base-pg-store.test.ts @@ -118,4 +118,15 @@ describe('BasePgStore', () => { expect(sqlTransactionContext.getStore()).toBeUndefined(); expect(db.sql).toEqual(obj); }); + + test('isConnected returns true when the connection is alive', async () => { + const connected = await db.isConnected(); + expect(connected).toBe(true); + }); + + test('isConnected returns false when the connection is not alive', async () => { + jest.spyOn(db, 'sql').mockRejectedValueOnce(new Error('Connection lost')); + const connected = await db.isConnected(); + expect(connected).toBe(false); + }); }); diff --git a/src/postgres/base-pg-store.ts b/src/postgres/base-pg-store.ts index 05d4585..aafed13 100644 --- a/src/postgres/base-pg-store.ts +++ b/src/postgres/base-pg-store.ts @@ -84,6 +84,19 @@ export abstract class BasePgStore { isProdEnv ? this.sql`CONCURRENTLY` : this.sql`` } ${this.sql(viewName)}`; } + + /** + * Checks if the database connection is alive. + * @returns True if connected, false otherwise. + */ + async isConnected(): Promise { + try { + await this.sql`SELECT NOW()`; + return true; + } catch (error) { + return false; + } + } } /** @@ -114,4 +127,7 @@ export abstract class BasePgStoreModule { async refreshMaterializedView(viewName: string): Promise { return this.parent.refreshMaterializedView(viewName); } + async isConnected(): Promise { + return this.parent.isConnected(); + } }