Skip to content
Open
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
31 changes: 31 additions & 0 deletions .github/workflows/dependency-diff-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: dependency-diff-comment

on:
workflow_run:
workflows: ['dependency-diff']
types:
- completed

permissions:
pull-requests: write
actions: read

jobs:
dependency-diff-comment:
name: 💬 Dependency diff comment
runs-on: ubuntu-slim
if: github.event.workflow_run.conclusion == 'success'

steps:
- name: 📥 Download artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: e18e-diff-result
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: 💬 Post comment
uses: e18e/action-dependency-diff@f825d5b5c5ce0a42dc46c47ec20de24460affcd8 # v1.5.0
with:
mode: comment-from-artifact
artifact-path: e18e-diff-result.json
47 changes: 47 additions & 0 deletions .github/workflows/dependency-diff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: dependency-diff

on:
pull_request:
branches:
- main
- release
paths:
- package.json
- pnpm-lock.yaml
- pnpm-workspace.yaml
- docs/package.json
- cli/package.json

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
cancel-in-progress: true

permissions:
contents: read

jobs:
dependency-diff:
name: 🔎 Dependency diff
runs-on: ubuntu-slim

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- name: 🔎 Compare dependencies
id: analyze
uses: e18e/action-dependency-diff@f825d5b5c5ce0a42dc46c47ec20de24460affcd8 # v1.5.0
with:
mode: artifact
detect-replacements: 'true'
duplicate-threshold: '4'
dependency-threshold: '15'
size-threshold: '200000'

- name: 📤 Upload artifact
if: steps.analyze.outputs.artifact-path
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: e18e-diff-result
path: ${{ steps.analyze.outputs.artifact-path }}
69 changes: 0 additions & 69 deletions .github/workflows/remove-needs-review-on-review.yml

This file was deleted.

1 change: 1 addition & 0 deletions .vite-hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vp staged
9 changes: 7 additions & 2 deletions app/components/CollapsibleSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@ function toggle() {
}

const ariaLabel = computed(() => {
const action = isOpen.value ? 'Collapse' : 'Expand'
return props.title ? `${action} ${props.title}` : action
if (!props.title) {
return isOpen.value ? $t('common.collapse') : $t('common.expand')
}

return isOpen.value
? $t('common.collapse_with_name', { name: props.title })
: $t('common.expand_with_name', { name: props.title })
})
useHead({
style: [
Expand Down
2 changes: 1 addition & 1 deletion app/pages/compare.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ async function exportComparisonDataAsMarkdown() {
}

defineOgImageComponent('Compare', {
packages: () => packages.value,
packages: () => packages.value.toSorted((a, b) => a.localeCompare(b)),
emptyDescription: () => $t('compare.packages.meta_description_empty'),
})

Expand Down
64 changes: 19 additions & 45 deletions cli/src/npm-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ const AUTH_URL_TIMEOUT_MS = 90_000
export interface ExecNpmOptions {
otp?: string
silent?: boolean
/** Working directory for the npm command. */
cwd?: string
/** When true, use PTY-based interactive execution instead of execFile. */
interactive?: boolean
/** When true, npm opens auth URLs in the user's browser.
Expand Down Expand Up @@ -214,6 +216,7 @@ async function execNpmInteractive(
name: 'xterm-256color',
cols: 120,
rows: 30,
cwd: options.cwd,
env,
})

Expand Down Expand Up @@ -337,6 +340,7 @@ async function execNpm(args: string[], options: ExecNpmOptions = {}): Promise<Np
const { command, args: processArgs } = resolveNpmProcessCommand(npmArgs)
const { stdout, stderr } = await execFileAsync(command, processArgs, {
timeout: 60000,
cwd: options.cwd,
env: createNpmEnv(),
})

Expand Down Expand Up @@ -559,12 +563,12 @@ export async function listUserPackages(user: string): Promise<NpmExecResult> {
* Creates a minimal package.json in a temp directory and publishes it.
* @param name Package name to claim
* @param author npm username of the publisher (for author field)
* @param otp Optional OTP for 2FA
* @param options Execution options (otp, interactive, etc.)
*/
export async function packageInit(
name: string,
author?: string,
otp?: string,
options?: ExecNpmOptions,
): Promise<NpmExecResult> {
validatePackageName(name)

Expand Down Expand Up @@ -600,52 +604,22 @@ export async function packageInit(
args.push('--access', access)
}

// Run npm publish from the temp directory
const npmArgs = otp ? [...args, '--otp', otp] : args

// Log the command being run (hide OTP value for security)
const displayCmd = otp ? `npm ${args.join(' ')} --otp ******` : `npm ${args.join(' ')}`
const displayCmd = options?.otp
? ['npm', ...args, '--otp', '******'].join(' ')
: ['npm', ...args].join(' ')
logCommand(`${displayCmd} (in temp dir for ${name})`)

try {
const { command, args: processArgs } = resolveNpmProcessCommand(npmArgs)
const { stdout, stderr } = await execFileAsync(command, processArgs, {
timeout: 60000,
cwd: tempDir.path,
env: createNpmEnv(),
})
const result = await execNpm(args, { ...options, cwd: tempDir.path, silent: true })

if (result.exitCode === 0) {
logSuccess(`Published ${name}@0.0.0`)

return {
stdout: stdout.trim(),
stderr: filterNpmWarnings(stderr),
exitCode: 0,
}
} catch (error) {
const err = error as { stdout?: string; stderr?: string; code?: number }
const stderr = err.stderr?.trim() ?? String(error)
const requiresOtp = detectOtpRequired(stderr)
const authFailure = detectAuthFailure(stderr)

if (requiresOtp) {
logError('OTP required')
} else if (authFailure) {
logError('Authentication required - please run "npm login" and restart the connector')
} else {
logError(filterNpmWarnings(stderr).split('\n')[0] || 'Command failed')
}

return {
stdout: err.stdout?.trim() ?? '',
stderr: requiresOtp
? 'This operation requires a one-time password (OTP).'
: authFailure
? 'Authentication failed. Please run "npm login" and restart the connector.'
: filterNpmWarnings(stderr),
exitCode: err.code ?? 1,
requiresOtp,
authFailure,
}
} else if (result.requiresOtp) {
logError('OTP required')
} else if (result.authFailure) {
logError('Authentication required - please run "npm login" and restart the connector')
} else {
logError(result.stderr.split('\n')[0] || 'Command failed')
}

return result
}
4 changes: 1 addition & 3 deletions cli/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -809,9 +809,7 @@ async function executeOperation(
result = await ownerRemove(params.user, params.pkg, execOptions)
break
case 'package:init':
// package:init has its own special execution path (temp dir + publish)
// and does not support interactive mode
result = await packageInit(params.name, params.author, options.otp)
result = await packageInit(params.name, params.author, execOptions)
break
default:
return {
Expand Down
Loading
Loading