diff --git a/dist/index.js b/dist/index.js index 94773fa0..4af9a575 100644 --- a/dist/index.js +++ b/dist/index.js @@ -105018,7 +105018,7 @@ function buildReusableUserData({ runnerVersion, owner, repo, label, githubRegist 'UD=$(curl -fsS -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" "http://169.254.169.254/latest/user-data" 2>/dev/null || true)', 'GH_TOKEN=$(printf \'%s\\n\' "$UD" | sed -n "s/^GH_TOKEN=\'\\(.*\\)\'$/\\1/p" | head -n1)', 'GH_LABEL=$(printf \'%s\\n\' "$UD" | sed -n "s/^GH_LABEL=\'\\(.*\\)\'$/\\1/p" | head -n1)', - 'GH_REPO_URL=$(printf \'%s\\n\' "$UD" | sed -n "s#^GH_REPO_URL=\'\\(.*\\)\'$#\\1#p" | head -n1)', + 'GH_REPO_URL=$(printf \'%s\\n\' "$UD" | sed -n "s,^GH_REPO_URL=\'\\(.*\\)\'$,\\1,p" | head -n1)', 'cd /home/runner/actions-runner', 'rm -f .runner .credentials .credentials_rsaparams', 'gh_runner_phone_home configuring', diff --git a/src/aws.js b/src/aws.js index 47646298..683af1df 100644 --- a/src/aws.js +++ b/src/aws.js @@ -368,7 +368,7 @@ function buildReusableUserData({ runnerVersion, owner, repo, label, githubRegist 'UD=$(curl -fsS -H "X-aws-ec2-metadata-token: $IMDS_TOKEN" "http://169.254.169.254/latest/user-data" 2>/dev/null || true)', 'GH_TOKEN=$(printf \'%s\\n\' "$UD" | sed -n "s/^GH_TOKEN=\'\\(.*\\)\'$/\\1/p" | head -n1)', 'GH_LABEL=$(printf \'%s\\n\' "$UD" | sed -n "s/^GH_LABEL=\'\\(.*\\)\'$/\\1/p" | head -n1)', - 'GH_REPO_URL=$(printf \'%s\\n\' "$UD" | sed -n "s#^GH_REPO_URL=\'\\(.*\\)\'$#\\1#p" | head -n1)', + 'GH_REPO_URL=$(printf \'%s\\n\' "$UD" | sed -n "s,^GH_REPO_URL=\'\\(.*\\)\'$,\\1,p" | head -n1)', 'cd /home/runner/actions-runner', 'rm -f .runner .credentials .credentials_rsaparams', 'gh_runner_phone_home configuring', diff --git a/tests/warmpool.test.js b/tests/warmpool.test.js index 83ef7da5..dd169642 100644 --- a/tests/warmpool.test.js +++ b/tests/warmpool.test.js @@ -1,6 +1,8 @@ // Tests for warm-pool (reuse: stop) support: findStoppedPoolInstance strict // matching, warmStartInstance ordering, stop/cycle helpers, and the per-boot // re-registration hook in the generated user-data. +const { execFileSync } = require('child_process'); + const mockSend = jest.fn(); jest.mock('@aws-sdk/client-ec2', () => ({ EC2Client: jest.fn(() => ({ send: mockSend })), @@ -185,4 +187,35 @@ describe('buildUserData — reuse: stop variant', () => { expect(ud).not.toContain('gh-runner.service'); expect(ud).not.toContain('/opt/gh-runner-register.sh'); }); + + test('GH_REPO_URL extraction survives the sed-delimiter/$# collision (regression, #63)', () => { + const ud = aws.buildUserData({ ...args, reuse: 'stop' }); + + // Pull the real GH_REPO_URL=$(... sed ...) line straight out of the + // generated script, so this test exercises the actual shipped code + // rather than a hand-reimplementation of it. + const marker = 'GH_REPO_URL=$(printf'; + const start = ud.indexOf(marker); + expect(start).toBeGreaterThan(-1); + const end = ud.indexOf('\n', start); + expect(end).toBeGreaterThan(start); + const extractionLine = ud.slice(start, end); + + // Run that exact line under 'set -euo pipefail' with a controlled IMDS + // user-data value, the same shape the real registerScript reads via + // `UD=$(curl ... /latest/user-data)`. If the sed delimiter collides with + // bash's `$#` special parameter (as `#` does), the sed invocation is + // malformed, the pipeline fails, and the assignment aborts the script + // under `set -e` before GH_REPO_URL is ever printed. + const script = [ + '#!/bin/bash', + 'set -euo pipefail', + "UD=\"GH_REPO_URL='https://github.com/foo/bar'\"", + extractionLine, + 'printf \'%s\' "$GH_REPO_URL"', + ].join('\n'); + + const result = execFileSync('bash', ['-c', script]).toString(); + expect(result).toBe('https://github.com/foo/bar'); + }); });