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
54 changes: 54 additions & 0 deletions .github/scripts/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fs from 'fs';

const { default: initialize } = await import('./initialize-request.mjs');
const { default: complete } = await import('./approve-or-deny-request.mjs');
const { default: validateRequest } = await import('./validate-request.mjs');

import nock from 'nock';
nock.disableNetConnect();
Expand Down Expand Up @@ -65,6 +66,59 @@ test.after.each(() => {
// nothing to do here
});

test("Validate GitHub owner and repository names", function () {
const validRequests = [
{ owner: "a", repo: "a" },
{ owner: "a-b", repo: ".github" },
{ owner: "a".repeat(39), repo: "a".repeat(100) },
{ owner: "GitHub", repo: "repo_name-1.0" }
];

for (const request of validRequests) {
assert.equal(validateRequest(JSON.stringify(request)), request);
}
});

test("Reject invalid GitHub owner names", function () {
const invalidOwners = [
"",
"-owner",
"owner-",
"owner--name",
"owner_name",
"owner/name",
"owner name",
"a".repeat(40)
];

for (const owner of invalidOwners) {
assert.throws(() => validateRequest({ owner, repo: "repo" }), /Owner must/);
}
});

test("Reject invalid GitHub repository names", function () {
const invalidRepositories = [
"",
"repo/name",
"repo name",
"repo$name",
"répo",
"a".repeat(101)
];

for (const repo of invalidRepositories) {
assert.throws(() => validateRequest({ owner: "owner", repo }), /Repository must/);
}
});

test("Reject malformed request payloads", function () {
assert.throws(() => validateRequest("not json"), /valid JSON/);
assert.throws(() => validateRequest(null), /JSON object/);
assert.throws(() => validateRequest([]), /JSON object/);
assert.throws(() => validateRequest({ owner: "owner" }), /Repository must/);
assert.throws(() => validateRequest({ repo: "repo" }), /Owner must/);
});

// Fail the workflow because the repo already exists
test("Fail the workflow because the repo already exists", async function () {
let mock = nock("https://github.robandpdx.demo-stack.com/api/v3");
Expand Down
33 changes: 33 additions & 0 deletions .github/scripts/validate-request.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const OWNER_PATTERN = /^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/;
const REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+$/;

export default function validateRequest(payload) {
let request;

try {
request = typeof payload === 'string' ? JSON.parse(payload) : payload;
} catch {
throw new Error('Request payload must be valid JSON');
}

if (!request || typeof request !== 'object' || Array.isArray(request)) {
throw new Error('Request payload must be a JSON object');
}

validateOwner(request.owner);
validateRepository(request.repo);

return request;
}

function validateOwner(owner) {
if (typeof owner !== 'string' || owner.length < 1 || owner.length > 39 || !OWNER_PATTERN.test(owner)) {
throw new Error('Owner must be 1-39 ASCII letters, digits, or single hyphens, and cannot begin or end with a hyphen');
}
}

function validateRepository(repo) {
if (typeof repo !== 'string' || repo.length < 1 || repo.length > 100 || !REPOSITORY_PATTERN.test(repo)) {
throw new Error('Repository must be 1-100 ASCII letters, digits, periods, hyphens, or underscores');
}
}
28 changes: 20 additions & 8 deletions .github/workflows/approve-or-deny-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,25 @@ jobs:
payload: ${{ steps.issue_body_parser_request.outputs.payload }}
steps:
- name: Get JSON Data out of Issue Request
uses: peter-murray/issue-body-parser-action@v3
uses: peter-murray/issue-body-parser-action@6f0770bae810c272cbdccf6f817b0759a9d82647 # v3
id: issue_body_parser_request
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
issue_id: ${{ github.event.issue.number }}
payload_marker: request
fail_on_missing: false
- name: Check out request validator
if: steps.issue_body_parser_request.outputs.payload != 'NOT_FOUND'
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Validate requested repository
if: steps.issue_body_parser_request.outputs.payload != 'NOT_FOUND'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
REQUEST_PAYLOAD: ${{ steps.issue_body_parser_request.outputs.payload }}
with:
script: |
const { default: validateRequest } = await import('${{ github.workspace }}/.github/scripts/validate-request.mjs');
validateRequest(process.env.REQUEST_PAYLOAD);
approve-or-deny-request:
runs-on: self-hosted
needs: parse-issue
Expand All @@ -34,18 +46,18 @@ jobs:
REPO: ${{ fromJson(needs.parse-issue.outputs.payload).repo }}
REQUEST_VERSION: ${{ fromJson(needs.parse-issue.outputs.payload).version }}
run: |
if [ $REQUEST_VERSION == 'latest' ]; then
if [ "$REQUEST_VERSION" = 'latest' ]; then
echo "Finding latest release of $OWNER/$REPO..."
export VERSION=`curl https://api.github.com/repos/$OWNER/$REPO/releases/latest | jq -r .name`
VERSION=$(curl "https://api.github.com/repos/$OWNER/$REPO/releases/latest" | jq -r .name)
else
export VERSION=$REQUEST_VERSION
VERSION="$REQUEST_VERSION"
Comment thread
robandpdx marked this conversation as resolved.
fi
echo "VERSION: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Check out scripts
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
check-latest: true
Expand All @@ -54,7 +66,7 @@ jobs:
cd .github/scripts
npm install
- name: Approve or deny request
uses: actions/github-script@v7
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
VERSION: ${{ steps.get_version.outputs.version }}
REPO: ${{ fromJson(needs.parse-issue.outputs.payload).repo }}
Expand Down
30 changes: 21 additions & 9 deletions .github/workflows/initialize-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,25 @@ jobs:
payload: ${{ steps.issue_body_parser_request.outputs.payload }}
steps:
- name: Get JSON Data out of Issue Request
uses: peter-murray/issue-body-parser-action@v3
uses: peter-murray/issue-body-parser-action@6f0770bae810c272cbdccf6f817b0759a9d82647 # v3
id: issue_body_parser_request
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
issue_id: ${{ github.event.issue.number }}
payload_marker: request
fail_on_missing: false
- name: Check out request validator
if: steps.issue_body_parser_request.outputs.payload != 'NOT_FOUND'
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Validate requested repository
if: steps.issue_body_parser_request.outputs.payload != 'NOT_FOUND'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
REQUEST_PAYLOAD: ${{ steps.issue_body_parser_request.outputs.payload }}
with:
script: |
const { default: validateRequest } = await import('${{ github.workspace }}/.github/scripts/validate-request.mjs');
validateRequest(process.env.REQUEST_PAYLOAD);
initialize-request:
runs-on: self-hosted
needs: parse-issue
Expand All @@ -34,22 +46,22 @@ jobs:
REPO: ${{ fromJson(needs.parse-issue.outputs.payload).repo }}
REQUEST_VERSION: ${{ fromJson(needs.parse-issue.outputs.payload).version }}
run: |
if [ $REQUEST_VERSION == 'latest' ]; then
if [ "$REQUEST_VERSION" = 'latest' ]; then
echo "Finding latest release of $OWNER/$REPO..."
export VERSION=`curl https://api.github.com/repos/$OWNER/$REPO/releases/latest | jq -r .name`
VERSION=$(curl "https://api.github.com/repos/$OWNER/$REPO/releases/latest" | jq -r .name)
if [ "$VERSION" == "null" ]; then
Comment thread
robandpdx marked this conversation as resolved.
echo "No latest version found for $OWNER/$REPO"
exit 1
fi
else
export VERSION=$REQUEST_VERSION
VERSION="$REQUEST_VERSION"
fi
echo "VERSION: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Check out scripts
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
check-latest: true
Expand All @@ -58,7 +70,7 @@ jobs:
cd .github/scripts
npm install
- name: Create the repo ${{ fromJson(needs.parse-issue.outputs.payload).repo }}_${{ steps.get_version.outputs.version }} on GitHub Enterprise Server
uses: actions/github-script@v7
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
VERSION: ${{ steps.get_version.outputs.version }}
OWNER: ${{ fromJson(needs.parse-issue.outputs.payload).owner }}
Expand All @@ -81,7 +93,7 @@ jobs:
OWNER: ${{ fromJson(needs.parse-issue.outputs.payload).owner }}
REPO: ${{ fromJson(needs.parse-issue.outputs.payload).repo }}
run: |
git clone https://github.com/$OWNER/$REPO requested-action
git clone "https://github.com/$OWNER/$REPO" requested-action
- name: Push requested action to private repo in $ACTIONS_APPROVED_ORG org on GitHub Enterprise Server
env:
REPO: ${{ fromJson(needs.parse-issue.outputs.payload).repo }}
Expand Down
9 changes: 2 additions & 7 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ jobs:
working-directory: ./.github/scripts

steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 #v7.0.0
with:
node-version: '20'
check-latest: true
Expand All @@ -28,8 +28,3 @@ jobs:
working-directory: ${{ env.working-directory }}
- run: npm test
working-directory: ${{ env.working-directory }}
- uses: tintef/nyc-reporter-action@0.2.5
with:
GITHUB_TOKEN: ${{ github.token }}
SKIP_COVERAGE_FOLDER: true
WORKING_DIRECTORY: ${{ env.working-directory }}
9 changes: 5 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ jobs:
env:
working-directory: ./.github/scripts
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 #v7.0.0
with:
node-version: 20
cache: npm
node-version: '20'
check-latest: true
cache: 'npm'
cache-dependency-path: ${{ env.working-directory }}/package-lock.json
- run: npm ci
working-directory: ${{ env.working-directory }}
Expand Down