Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .github/dependabot.yml

This file was deleted.

78 changes: 42 additions & 36 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -1,41 +1,19 @@
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages

name: Node.js Package (npm)

on:
release:
types: [created]
push:
branches:
- working

permissions:
contents: write
packages: write
id-token: write

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- name: Install dependencies
run: |
# Use public npm registry (package-lock.json is gitignored)
npm config set registry https://registry.npmjs.org/
# Clean install to ensure platform-specific optional deps (rollup native bindings) are resolved correctly
# See: https://github.com/npm/cli/issues/4828
rm -rf node_modules package-lock.json
npm install --force
timeout-minutes: 10
- run: npm run lint
- run: npm run build
- run: npm run test

publish-npm:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand All @@ -44,16 +22,44 @@ jobs:
with:
node-version: 24
registry-url: https://registry.npmjs.org/
- name: Update npm
run: npm install -g npm@latest
- name: Install dependencies
- name: Determine npm tag and publish strategy
id: npm-tag
run: |
# Use public npm registry (package-lock.json is gitignored)
npm config set registry https://registry.npmjs.org/
# Clean install to ensure platform-specific optional deps (rollup native bindings) are resolved correctly
# See: https://github.com/npm/cli/issues/4828
rm -rf node_modules package-lock.json
npm install --force
timeout-minutes: 10
- run: npm publish --access public
VERSION=$(node -p "require('./package.json').version")
IS_RELEASE="${{ github.event_name == 'release' }}"
SHORT_SHA=$(git rev-parse --short=7 HEAD)
TIMESTAMP=$(date +%Y%m%d%H%M%S)

if [[ "$VERSION" == *"-"* ]]; then
echo "tag=dev" >> $GITHUB_OUTPUT
echo "should_publish=true" >> $GITHUB_OUTPUT
# Add timestamp and SHA to pre-release version (e.g., 1.5.5-dev.0 -> 1.5.5-dev.20260131210612.ab169e2)
BASE_VERSION="${VERSION%%-*}"
NEW_VERSION="${BASE_VERSION}-dev.${TIMESTAMP}.${SHORT_SHA}"
echo "version=${NEW_VERSION}" >> $GITHUB_OUTPUT
echo "📦 Publishing pre-release version: ${NEW_VERSION}"
else
echo "tag=latest" >> $GITHUB_OUTPUT
echo "version=${VERSION}" >> $GITHUB_OUTPUT
# Only publish production versions on release events
if [[ "$IS_RELEASE" == "true" ]]; then
echo "should_publish=true" >> $GITHUB_OUTPUT
echo "📦 Publishing production version: ${VERSION}"
else
echo "should_publish=false" >> $GITHUB_OUTPUT
echo "⚠️ Skipping publish: Production version detected on non-release push"
fi
fi
- run: npm install -g npm@latest
if: steps.npm-tag.outputs.should_publish == 'true'
- run: npm install --verbose --foreground-scripts
if: steps.npm-tag.outputs.should_publish == 'true'
timeout-minutes: 10
- name: Update package.json version for pre-release
if: steps.npm-tag.outputs.should_publish == 'true' && steps.npm-tag.outputs.tag == 'dev'
run: |
node -e "const pkg = require('./package.json'); pkg.version = '${{ steps.npm-tag.outputs.version }}'; require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');"
echo "Updated package.json to version ${{ steps.npm-tag.outputs.version }}"
- name: Publish to npm
if: steps.npm-tag.outputs.should_publish == 'true'
run: npm publish --access public --tag ${{ steps.npm-tag.outputs.tag }}
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ on:
branches:
- main

concurrency:
group: test-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true

permissions:
contents: write

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@grunnverk/github-tools",
"version": "1.5.4",
"version": "1.5.5",
"description": "GitHub API utilities for automation - PR management, issue tracking, workflow monitoring",
"main": "dist/index.js",
"type": "module",
Expand Down Expand Up @@ -45,7 +45,7 @@
"node": ">=24.0.0"
},
"dependencies": {
"@grunnverk/git-tools": "^1.5.4",
"@grunnverk/git-tools": "^1.5.14",
"@octokit/rest": "^22.0.0"
},
"peerDependencies": {
Expand Down
17 changes: 16 additions & 1 deletion src/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,9 +642,24 @@ export const waitForPullRequestChecks = async (prNumber: number, options: { time
consecutiveNoChecksCount = 0;

// ... rest of the while loop logic ...
// Filter for actual failures, excluding cancelled checks
// Cancelled checks are typically from workflows that cancel themselves (e.g., concurrency groups)
// and should not be treated as failures
const failingChecks = checkRuns.filter(
(cr) => cr.conclusion && ['failure', 'timed_out', 'cancelled'].includes(cr.conclusion)
(cr) => cr.conclusion && ['failure', 'timed_out'].includes(cr.conclusion)
);

// Track cancelled checks separately for informational purposes
const cancelledChecks = checkRuns.filter(
(cr) => cr.conclusion === 'cancelled'
);

if (cancelledChecks.length > 0) {
logger.info(`PR #${prNumber}: ${cancelledChecks.length} check${cancelledChecks.length > 1 ? 's' : ''} cancelled (not treated as failure):`);
for (const check of cancelledChecks) {
logger.info(` 🚫 ${check.name}: cancelled`);
}
}

if (failingChecks.length > 0) {
const { owner, repo } = await getRepoDetails(options.cwd);
Expand Down
15 changes: 7 additions & 8 deletions tests/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,16 +604,13 @@ describe('GitHub Utilities', () => {
},
});

// Mock check details for each failed check
// Mock check details for each failed check (only failure and timed_out, not cancelled)
mockOctokit.checks.get
.mockResolvedValueOnce({
data: { output: { title: 'Linting errors found', summary: 'Code style violations detected' } }
})
.mockResolvedValueOnce({
data: { output: { title: 'Build timeout', summary: 'Build took too long to complete' } }
})
.mockResolvedValueOnce({
data: { output: { title: 'Tests cancelled', summary: 'Test run was cancelled' } }
});

try {
Expand All @@ -623,8 +620,9 @@ describe('GitHub Utilities', () => {
const { PullRequestCheckError } = await import('../src/errors');
expect(error).toBeInstanceOf(PullRequestCheckError);
expect(error.prNumber).toBe(123);
expect(error.failedChecks).toHaveLength(3);
expect(error.message).toContain('3 checks failed');
// Only failure and timed_out are treated as failures, cancelled is not
expect(error.failedChecks).toHaveLength(2);
expect(error.message).toContain('2 checks failed');

const instructions = error.getRecoveryInstructions();
expect(instructions).toBeTruthy();
Expand Down Expand Up @@ -3958,7 +3956,8 @@ jobs:
for (const conclusionType of testConclusionTypes) {
mockOctokit.pulls.get.mockResolvedValue({ data: { head: { sha: 'test-sha' } } });

if (['failure', 'cancelled', 'timed_out'].includes(conclusionType)) {
// Only failure and timed_out should cause failures, cancelled is not treated as failure
if (['failure', 'timed_out'].includes(conclusionType)) {
// These should cause failures
vi.spyOn(GitHub, 'getRepoDetails').mockResolvedValue({ owner: 'test-owner', repo: 'test-repo' });
mockRun.mockImplementation(async (command: string) => {
Expand Down Expand Up @@ -3995,7 +3994,7 @@ jobs:
expect(error.failedChecks[0].conclusion).toBe(conclusionType);
}
} else {
// These should pass
// These should pass (including 'cancelled')
mockOctokit.checks.listForRef.mockResolvedValue({
data: {
check_runs: [{
Expand Down
8 changes: 2 additions & 6 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ export default defineConfig({
},
// Add pool configuration to prevent memory issues
pool: 'forks',
poolOptions: {
forks: {
maxForks: 2,
minForks: 1
}
},
maxForks: 2,
minForks: 1,
// Add test timeout and memory limits
testTimeout: 30000,
hookTimeout: 10000,
Expand Down
Loading