Skip to content
Closed
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
29 changes: 18 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Commitlint
uses: wagoid/commitlint-github-action@v4
uses: wagoid/commitlint-github-action@v6
with:
failOnWarnings: true

- name: Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v6
with:
node-version: '16'
node-version: '24'

- name: Install Dependencies
run: yarn
Expand All @@ -38,13 +38,20 @@ jobs:

- name: Verify Package
run: |
# If there are modifications to the files in `dist/` after the buid step the latest build wasn't pushed
if git --no-pager diff --name-only | grep '^dist/' || git ls-files --other --exclude-standard | grep '^dist/'; then
echo "The build action code doesn't match what should have been built."
echo "Rebuild the action by running 'yarn build', commit the changes, and push again."
git --no-pager diff --name-only | grep '^dist/'
git ls-files --other --exclude-standard | grep '^dist/'
exit 1
# Check for modifications in the 'dist/' directory after the build step.
# Step 1: Get the list of changed files.
changed_files=$(git status --porcelain | awk '{print $2}')

# Step 2: Filter for files in 'dist/'.
dist_changes=$(echo "$changed_files" | grep '^dist/' 2>/dev/null || true)

# Step 3: If there are changes, print a helpful message and fail the job.
if [ -n "$dist_changes" ]; then
echo "The build action code doesn't match what should have been built."
echo "Rebuild the action by running 'yarn build', commit the changes, and push again."
echo "Changed files in dist/:"
echo "$dist_changes"
exit 1
fi

- name: Run the Action Package
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v16.18.0
v24
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: 'git-changesets'
description: 'Github Action to determine changed files (aka changesets)'
author: 'Collin Miller'
runs:
using: node16
using: node24
main: dist/index.js
inputs:
token:
Expand Down
15 changes: 0 additions & 15 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap

"use strict";

var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
const main_1 = __importDefault(__nccwpck_require__(3109));
(0, main_1.default)();


/***/ }),

/***/ 3109:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {

"use strict";

var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/main.test.ts → src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as core from '@actions/core';
import nock from 'nock';

import cp from 'child_process';
import run from './main';
import run from '.';

const originalGitHubWorkspace = process.env.GITHUB_WORKSPACE;

Expand Down
211 changes: 209 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,210 @@
import run from './main';
import * as core from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { execSync } from 'child_process';

run();
enum OutputFormat {
SpaceDelimited = 'space-delimited',
Csv = 'csv',
Json = 'json',
}

enum FileStatus {
Added = 'added',
Modified = 'modified',
Removed = 'removed',
Renamed = 'renamed',
}

interface IGithubResponseFiles {
status: string;
filename: string;
}
interface IGithubResponse {
status: number;
data: {
status: string;
files: IGithubResponseFiles[];
};
}

const setFormat = (elements: string[], outputFormat: OutputFormat) => {
if (outputFormat === OutputFormat.SpaceDelimited) {
return elements.join(' ');
}
if (outputFormat === OutputFormat.Csv) {
return elements.join(',');
}
return JSON.stringify(elements);
};

const setOutput = (
added: string[],
modified: string[],
removed: string[],
renamed: string[],
outputFormat: OutputFormat,
) => {
const allFormatted = setFormat([...added, ...modified, ...removed, ...renamed], outputFormat);
const addedFormatted = setFormat(added, outputFormat);
const modifiedFormatted = setFormat(modified, outputFormat);
const removedFormatted = setFormat(removed, outputFormat);
const renamedFormatted = setFormat(renamed, outputFormat);
const addedModifiedFormatted = setFormat([...added, ...modified], outputFormat);
// Log the output values.
core.info(`All: ${allFormatted}`);
core.info(`Added: ${addedFormatted}`);
core.info(`Modified: ${modifiedFormatted}`);
core.info(`Removed: ${removedFormatted}`);
core.info(`Renamed: ${renamedFormatted}`);
core.info(`Added or modified: ${addedModifiedFormatted}`);

// Set step output context.
core.setOutput('all', allFormatted);
core.setOutput('added', addedFormatted);
core.setOutput('modified', modifiedFormatted);
core.setOutput('removed', removedFormatted);
core.setOutput('renamed', renamedFormatted);
core.setOutput('added_modified', addedModifiedFormatted);
};

const parseCommit = async (commitSha: string): Promise<IGithubResponse> => {
enum GitFileStatus {
Added = 'A',
Modified = 'M',
Deleted = 'D',
Renamed = 'R',
}
const files: IGithubResponseFiles[] = [];

try {
const result = await execSync(`git --no-pager diff HEAD~1 --name-status ${commitSha}`).toString('utf-8');
result.split('\n').forEach((element: string) => {
const fileStatus: string = element.split('\t')[0];
const fileName: string = element.split('\t')[1];
if (fileStatus && fileName) {
const data = {} as IGithubResponseFiles;
data.filename = fileName;
if (fileStatus === GitFileStatus.Added) {
data.status = FileStatus.Added;
} else if (fileStatus === GitFileStatus.Modified) {
data.status = FileStatus.Modified;
} else if (fileStatus === GitFileStatus.Deleted) {
data.status = FileStatus.Removed;
} else if (fileStatus.startsWith(GitFileStatus.Renamed)) {
data.status = FileStatus.Renamed;
}
files.push(data as IGithubResponseFiles);
}
});
} catch (error) {
const err: Error = error as Error;
core.setFailed(`Exception raised while parsing commit ${commitSha}, message ${err.message}`);
}
return { status: 200, data: { files } } as IGithubResponse;
};

const run = async (): Promise<void> => {
try {
// Create GitHub client with the API token.
const format = core.getInput('format', {
required: true,
}) as OutputFormat;

// Ensure that the format parameter is set properly.
if (!Object.values(OutputFormat).includes(format)) {
core.setFailed(
`Output format must be one of must be one of ${Object.values(OutputFormat).join(
', ',
)}, got '${format}'.`,
);
}

// Debug log the payload.
core.debug(`Payload keys: ${Object.keys(context.payload)}`);
const client = getOctokit(core.getInput('token', { required: true }));

// Get event name.
const { eventName } = context;

// Define the base and head commits to be extracted from the payload.
let base: string;
let head: string;

if (eventName === 'pull_request') {
base = context.payload.pull_request?.base?.ref;
head = context.payload.pull_request?.head?.sha;
} else if (eventName === 'push') {
base = context.payload.before;
head = context.payload.after;
} else {
base = '';
head = '';
core.setFailed(
`Pull requests and pushes are the only supported event types. Event type: ${context.eventName}`,
);
}

// Log the commits
core.info(`Base commit: ${base}`);
core.info(`Head commit: ${head}`);
let response;
// Ensure that the base and head properties are set on the payload.
if (!base || !head) {
core.setFailed(
`The base and head commits are missing from the payload for this ${context.eventName} event.`,
);
} else if (base === '0000000000000000000000000000000000000000') {
response = await parseCommit(head);
} else {
// https://developer.github.com/v3/repos/commits/#compare-two-commits
response = await client.rest.repos.compareCommits({
base,
head,
owner: context.repo.owner,
repo: context.repo.repo,
});
}

// Ensure that the request was successful.
if (response?.status !== 200) {
core.setFailed(
`The GitHub API for comparing the base and head commits for this ${context.eventName} event returned ${response?.status}, expected 200.`,
);
}

const files = response?.data.files;

const added = [] as string[];
const modified = [] as string[];
const removed = [] as string[];
const renamed = [] as string[];
files?.forEach((element) => {
if (element.status === FileStatus.Added) {
added.push(element.filename);
} else if (element.status === FileStatus.Modified) {
modified.push(element.filename);
} else if (element.status === FileStatus.Removed) {
removed.push(element.filename);
} else if (element.status === FileStatus.Renamed) {
renamed.push(element.filename);
} else {
core.setFailed(
`Invalid File Status '${element.status}', expected 'added', 'modified', 'removed', or 'renamed'. File causing violation ${element.filename}`,
);
}
// If we're using the 'space-delimited' format and any of the filenames have a space in them,
// then fail the step.
if (format === OutputFormat.SpaceDelimited && element.filename.includes(' ')) {
core.setFailed(
`Filenames cannot contain a space when using the 'space-delimited' option. Filename causing the violation '${element.filename}'`,
);
}
});
setOutput(added, modified, removed, renamed, format);
} catch (error) {
const err: Error = error as Error;
core.setFailed(err.message);
}
};

export default run;
Loading
Loading