-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.ts
170 lines (143 loc) · 5.12 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import { getBooleanInput, getInput } from '@actions/core';
import { error, info } from 'console';
import { writeFile } from 'fs-extra';
import fetch, { Response, fileFrom, FormData } from 'node-fetch';
// https://www.conventionalcommits.org/en/v1.0.0/
const CONVENTIONAL_COMMIT_REGEX = new RegExp(
/^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(.*\))?(!?): .*$/,
);
export const isValidConventionalCommitMessage = (message: string) => {
return CONVENTIONAL_COMMIT_REGEX.test(message);
};
// Detect if running in GitHub Actions or GitLab CI
function isGitLabCI(): boolean {
return process.env['GITLAB_CI'] === 'true';
}
// Get input values from either GitHub Actions or GitLab CI environment
function getInputValue(name: string, options?: { required: boolean }): string {
if (isGitLabCI()) {
// Try GitLab-specific INPUT_ prefixed variable first (like GitHub Actions)
const inputEnvName = `INPUT_${name.toUpperCase()}`;
const inputValue = process.env[inputEnvName];
// Fall back to direct name for backward compatibility
const directEnvName = name.toUpperCase();
const directValue = process.env[directEnvName];
const value = inputValue || directValue;
if (options?.required && !value) {
throw new Error(`Input required and not supplied: ${name}`);
}
return value || '';
} else {
return getInput(name, options);
}
}
// Get boolean input values from either GitHub Actions or GitLab CI environment
function getBooleanInputValue(name: string, options?: { required: boolean }): boolean {
if (isGitLabCI()) {
// Try GitLab-specific INPUT_ prefixed variable first (like GitHub Actions)
const inputEnvName = `INPUT_${name.toUpperCase()}`;
const inputValue = process.env[inputEnvName]?.toLowerCase();
// Fall back to direct name for backward compatibility
const directEnvName = name.toUpperCase();
const directValue = process.env[directEnvName]?.toLowerCase();
const value = inputValue || directValue;
if (options?.required && value === undefined) {
throw new Error(`Input required and not supplied: ${name}`);
}
return value === 'true';
} else {
return getBooleanInput(name, options);
}
}
export async function main() {
// inputs
const stainless_api_key = getInputValue('stainless_api_key', { required: true });
const inputPath = getInputValue('input_path', { required: true });
const configPath = getInputValue('config_path', { required: false });
const projectName = getInputValue('project_name', { required: false });
const commitMessage = getInputValue('commit_message', { required: false });
const guessConfig = getBooleanInputValue('guess_config', { required: false });
const branch = getInputValue('branch', { required: false });
const outputPath = getInputValue('output_path');
if (configPath && guessConfig) {
const errorMsg = "Can't set both configPath and guessConfig";
error(errorMsg);
throw Error(errorMsg);
}
if (commitMessage && !isValidConventionalCommitMessage(commitMessage)) {
const errorMsg =
'Invalid commit message format. Please follow the Conventional Commits format: https://www.conventionalcommits.org/en/v1.0.0/';
error(errorMsg);
throw Error(errorMsg);
}
info(configPath ? 'Uploading spec and config files...' : 'Uploading spec file...');
const response = await uploadSpecAndConfig(
inputPath,
configPath,
stainless_api_key,
projectName,
commitMessage,
guessConfig,
branch,
);
if (!response.ok) {
const text = await response.text();
const errorMsg = `Failed to upload files: ${response.statusText} ${text}`;
error(errorMsg);
throw Error(errorMsg);
}
info('Uploaded!');
if (outputPath) {
const decoratedSpec = await response.text();
writeFile(outputPath, decoratedSpec);
info('Wrote decorated spec to', outputPath);
}
}
async function uploadSpecAndConfig(
specPath: string,
configPath: string,
token: string,
projectName: string,
commitMessage: string,
guessConfig: boolean,
branch: string,
): Promise<Response> {
const formData = new FormData();
formData.set('projectName', projectName);
if (commitMessage) {
formData.set('commitMessage', commitMessage);
}
// append a spec file
formData.set('oasSpec', await fileFrom(specPath, 'text/plain'));
// append a config file, if present
if (configPath) {
formData.set('stainlessConfig', await fileFrom(configPath, 'text/plain'));
}
if (guessConfig) {
formData.set('guessConfig', 'true');
}
if (branch) {
formData.set('branch', branch);
}
// Determine which CI system is being used for headers
const headers: Record<string, string> = {
Authorization: `Bearer ${token}`,
};
if (isGitLabCI()) {
headers['X-GitLab-CI'] = 'stainless-api/upload-openapi-spec-action';
} else {
headers['X-GitHub-Action'] = 'stainless-api/upload-openapi-spec-action';
}
const response = await fetch('https://api.stainless.com/api/spec', {
method: 'POST',
body: formData,
headers,
});
return response;
}
if (require.main === module) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}