Skip to content

Commit 1f8a7ad

Browse files
authored
Architectural Refactoring & Type-Safe GitHub Action (#6)
* chore(config): add ConfigManager with centralized validation and immutable typing - adds src/config.ts containing the configmanager class. - implements typed input reading via core.getinput. - introduces ActionInputs interface with readonly properties to ensure immutability. - centralizes validation rules (regex, nullability), following clean code and fail fast standards. - acts as an adapter mapping external inputs (snake_case) to internal camelcase properties. * refactor(core): transform index.ts into a pure action orchestrator - removes duplicate logic for validation and input reading. - uses configmanager to securely retrieve all configurations. - maintains only the orchestration flow (bootstrap → exec), following srp. - improves architectural clarity and prepares the module for future extensions. * build: update compiled artifacts in dist/ - updates dist/index.js to reflect the new modular architecture. - synchronizes the build after refactoring index.ts and adding config.ts. - no extra functional changes beyond the typescript output. * documenting...
1 parent 7ae009e commit 1f8a7ad

5 files changed

Lines changed: 121 additions & 62 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ jobs:
5252
runs-on: ubuntu-latest
5353
steps:
5454
- name: Exec Branch Shift
55-
uses: ws2git/branch-shift@v1
55+
uses: ws2git/branch-shift@v1.5
5656

5757
with:
58-
github_token: ${{ secrets.YOUR_PAT }}
58+
github-token: ${{ secrets.YOUR_PAT }}
5959
owner: ${{ github.event.inputs.owner }}
6060
repo: ${{ github.event.inputs.repo }}
6161
branch: ${{ github.event.inputs.branch }}
@@ -67,7 +67,7 @@ jobs:
6767

6868
| Name | Required | Description |
6969
|---|---|---|
70-
| `github_token` | Yes | GitHub Token with `repo` scope to authorize the API call. |
70+
| `github-token` | Yes | GitHub Token with `repo` scope to authorize the API call. |
7171
| `owner` | Yes | The owner of the repository (e.g., your-organization or your-username). |
7272
| `repo` | Yes | The name of the repository (e.g., your-repo). |
7373
| `branch` | Yes | The current name of the branch to be renamed. |

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ branding:
55
icon: 'git-branch'
66
color: 'orange'
77
inputs:
8-
github_token:
8+
github-token:
99
description: 'GitHub Token'
1010
required: true
1111
owner:

dist/index.js

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29922,7 +29922,7 @@ function wrappy (fn, cb) {
2992229922

2992329923
/***/ }),
2992429924

29925-
/***/ 9407:
29925+
/***/ 2973:
2992629926
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
2992729927

2992829928
"use strict";
@@ -29961,36 +29961,87 @@ var __importStar = (this && this.__importStar) || (function () {
2996129961
};
2996229962
})();
2996329963
Object.defineProperty(exports, "__esModule", ({ value: true }));
29964+
exports.ConfigManager = void 0;
2996429965
const core = __importStar(__nccwpck_require__(7484));
29965-
const github = __importStar(__nccwpck_require__(3228));
29966-
/**
29967-
* Helper function to validate inputs before execution.
29968-
* Throws an error if any validation fails.
29969-
*/
29970-
function validateInputs(inputs) {
29971-
const { owner, repo, branch, newName, githubToken } = inputs;
29972-
if (!owner || !repo || !branch || !newName || !githubToken) {
29973-
throw new Error('All input fields (owner, repo, branch, new_name) are required.');
29966+
class ConfigManager {
29967+
static getInputs() {
29968+
const inputs = {
29969+
owner: core.getInput('owner', { required: true }).trim(),
29970+
repo: core.getInput('repo', { required: true }).trim(),
29971+
branch: core.getInput('branch', { required: true }).trim(),
29972+
newName: core.getInput('new_name', { required: true }).trim(),
29973+
githubToken: core.getInput('github-token', { required: true }).trim(),
29974+
};
29975+
this.validate(inputs);
29976+
return inputs;
2997429977
}
29975-
const branchRegex = /^[a-zA-Z0-9-_./]+$/;
29976-
if (!branchRegex.test(newName)) {
29977-
throw new Error(`The new branch name is invalid: '${newName}'. Allowed characters: a-z, A-Z, 0-9, -, _, ., /`);
29978+
static validate(inputs) {
29979+
const { owner, repo, branch, newName, githubToken } = inputs;
29980+
if (!owner || !repo || !branch || !newName || !githubToken) {
29981+
throw new Error('All inputs are required and cannot be empty.');
29982+
}
29983+
const branchRegex = /^[a-zA-Z0-9-_./]+$/;
29984+
if (!branchRegex.test(newName)) {
29985+
throw new Error(`Invalid branch name format: '${newName}'.`);
29986+
}
2997829987
}
2997929988
}
29989+
exports.ConfigManager = ConfigManager;
29990+
29991+
29992+
/***/ }),
29993+
29994+
/***/ 9407:
29995+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
29996+
29997+
"use strict";
29998+
29999+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30000+
if (k2 === undefined) k2 = k;
30001+
var desc = Object.getOwnPropertyDescriptor(m, k);
30002+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30003+
desc = { enumerable: true, get: function() { return m[k]; } };
30004+
}
30005+
Object.defineProperty(o, k2, desc);
30006+
}) : (function(o, m, k, k2) {
30007+
if (k2 === undefined) k2 = k;
30008+
o[k2] = m[k];
30009+
}));
30010+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30011+
Object.defineProperty(o, "default", { enumerable: true, value: v });
30012+
}) : function(o, v) {
30013+
o["default"] = v;
30014+
});
30015+
var __importStar = (this && this.__importStar) || (function () {
30016+
var ownKeys = function(o) {
30017+
ownKeys = Object.getOwnPropertyNames || function (o) {
30018+
var ar = [];
30019+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30020+
return ar;
30021+
};
30022+
return ownKeys(o);
30023+
};
30024+
return function (mod) {
30025+
if (mod && mod.__esModule) return mod;
30026+
var result = {};
30027+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
30028+
__setModuleDefault(result, mod);
30029+
return result;
30030+
};
30031+
})();
30032+
Object.defineProperty(exports, "__esModule", ({ value: true }));
30033+
const core = __importStar(__nccwpck_require__(7484));
30034+
const github = __importStar(__nccwpck_require__(3228));
30035+
const config_1 = __nccwpck_require__(2973);
2998030036
/**
2998130037
* Main asynchronous function.
2998230038
* @returns {Promise<void>}
2998330039
*/
2998430040
async function run() {
2998530041
try {
29986-
const inputs = {
29987-
githubToken: core.getInput('github_token', { required: true }).trim(),
29988-
owner: core.getInput('owner', { required: true }).trim(),
29989-
repo: core.getInput('repo', { required: true }).trim(),
29990-
branch: core.getInput('branch', { required: true }).trim(),
29991-
newName: core.getInput('new_name', { required: true }).trim(),
29992-
};
29993-
validateInputs(inputs);
30042+
core.info('-> Initializing action...');
30043+
const inputs = config_1.ConfigManager.getInputs();
30044+
core.info('✅ Inputs validated.');
2999430045
const octokit = github.getOctokit(inputs.githubToken);
2999530046
core.info(`Starting the renaming of '${inputs.branch}' to '${inputs.newName}'...`);
2999630047
await octokit.request('POST /repos/{owner}/{repo}/branches/{branch}/rename', {
@@ -30002,7 +30053,7 @@ async function run() {
3000230053
'X-GitHub-Api-Version': '2022-11-28',
3000330054
},
3000430055
});
30005-
core.info(`✅ Branch renamed successfully.`);
30056+
core.info(`✅ Branch renomeada com sucesso.`);
3000630057
}
3000730058
catch (error) {
3000830059
if (error instanceof Error) {

src/config.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as core from '@actions/core';
2+
3+
export interface ActionInputs {
4+
readonly owner: string;
5+
readonly repo: string;
6+
readonly branch: string;
7+
readonly newName: string;
8+
readonly githubToken: string;
9+
}
10+
11+
export class ConfigManager {
12+
static getInputs(): ActionInputs {
13+
const inputs: ActionInputs = {
14+
owner: core.getInput('owner', { required: true }).trim(),
15+
repo: core.getInput('repo', { required: true }).trim(),
16+
branch: core.getInput('branch', { required: true }).trim(),
17+
newName: core.getInput('new_name', { required: true }).trim(),
18+
githubToken: core.getInput('github-token', { required: true }).trim(),
19+
};
20+
21+
this.validate(inputs);
22+
return inputs;
23+
}
24+
25+
private static validate(inputs: ActionInputs): void {
26+
const { owner, repo, branch, newName, githubToken } = inputs;
27+
28+
if (!owner || !repo || !branch || !newName || !githubToken) {
29+
throw new Error('All inputs are required and cannot be empty.');
30+
}
31+
32+
const branchRegex = /^[a-zA-Z0-9-_./]+$/;
33+
if (!branchRegex.test(newName)) {
34+
throw new Error(`Invalid branch name format: '${newName}'.`);
35+
}
36+
37+
}
38+
}

src/index.ts

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,17 @@
11
import * as core from '@actions/core';
22
import * as github from '@actions/github';
3-
4-
interface ActionInputs {
5-
readonly githubToken: string;
6-
readonly owner: string;
7-
readonly repo: string;
8-
readonly branch: string;
9-
readonly newName: string;
10-
}
11-
12-
/**
13-
* Helper function to validate inputs before execution.
14-
* Throws an error if any validation fails.
15-
*/
16-
function validateInputs(inputs: ActionInputs): void {
17-
const { owner, repo, branch, newName, githubToken } = inputs;
18-
19-
if (!owner || !repo || !branch || !newName || !githubToken) {
20-
throw new Error('All input fields (owner, repo, branch, new_name) are required.');
21-
}
22-
23-
const branchRegex = /^[a-zA-Z0-9-_./]+$/;
24-
if (!branchRegex.test(newName)) {
25-
throw new Error(`The new branch name is invalid: '${newName}'. Allowed characters: a-z, A-Z, 0-9, -, _, ., /`);
26-
}
27-
}
28-
3+
import { ConfigManager } from './config';
294

305
/**
316
* Main asynchronous function.
327
* @returns {Promise<void>}
338
*/
349
async function run(): Promise<void> {
3510
try {
36-
const inputs: ActionInputs = {
37-
githubToken: core.getInput('github_token', { required: true }).trim(),
38-
owner: core.getInput('owner', { required: true }).trim(),
39-
repo: core.getInput('repo', { required: true }).trim(),
40-
branch: core.getInput('branch', { required: true }).trim(),
41-
newName: core.getInput('new_name', { required: true }).trim(),
42-
};
43-
44-
validateInputs(inputs);
11+
core.info('-> Initializing action...');
12+
13+
const inputs = ConfigManager.getInputs();
14+
core.info('✅ Inputs validated.');
4515

4616
const octokit = github.getOctokit(inputs.githubToken);
4717

@@ -57,7 +27,7 @@ async function run(): Promise<void> {
5727
},
5828
});
5929

60-
core.info(`✅ Branch renamed successfully.`);
30+
core.info(`✅ Branch renomeada com sucesso.`);
6131

6232
} catch (error: unknown) {
6333
if (error instanceof Error) {

0 commit comments

Comments
 (0)