@scrymore/scry-deployer 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,47 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const CONFIG_DIR = path.join(os.homedir(), '.scry');
6
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
7
+
8
+ function ensureConfigDir() {
9
+ if (!fs.existsSync(CONFIG_DIR)) {
10
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
11
+ }
12
+ }
13
+
14
+ function loadConfig() {
15
+ if (!fs.existsSync(CONFIG_FILE)) {
16
+ return {};
17
+ }
18
+ try {
19
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
20
+ } catch (error) {
21
+ return {};
22
+ }
23
+ }
24
+
25
+ function saveConfig(config) {
26
+ ensureConfigDir();
27
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
28
+ }
29
+
30
+ function getAuthToken() {
31
+ const config = loadConfig();
32
+ return config.authToken;
33
+ }
34
+
35
+ function setAuthToken(token) {
36
+ const config = loadConfig();
37
+ config.authToken = token;
38
+ saveConfig(config);
39
+ }
40
+
41
+ module.exports = {
42
+ loadConfig,
43
+ saveConfig,
44
+ getAuthToken,
45
+ setAuthToken,
46
+ CONFIG_FILE
47
+ };
package/lib/config.js ADDED
@@ -0,0 +1,217 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ /**
6
+ * Default configuration for storybook-deployer
7
+ */
8
+ const DEFAULT_CONFIG = {
9
+ apiUrl: 'https://api.default-service.com/v1',
10
+ verbose: false,
11
+ // Users should set these values
12
+ apiKey: '',
13
+ dir: './storybook-static',
14
+ // Project and version for deployment
15
+ project: '',
16
+ version: '',
17
+ // Analysis options
18
+ withAnalysis: false,
19
+ storiesDir: null, // null enables auto-detection of .stories.* files
20
+ screenshotsDir: './__screenshots__',
21
+ storybookUrl: '',
22
+ storycapOptions: {
23
+ chromiumPath: '',
24
+ outDir: './__screenshots__',
25
+ parallel: undefined,
26
+ delay: undefined,
27
+ include: undefined,
28
+ exclude: undefined,
29
+ omitBackground: true
30
+ }
31
+ };
32
+
33
+ /**
34
+ * Get the config file path in the project directory
35
+ */
36
+ function getConfigPath() {
37
+ return path.join(process.cwd(), '.storybook-deployer.json');
38
+ }
39
+
40
+ /**
41
+ * Create a default config file if it doesn't exist
42
+ */
43
+ function createDefaultConfig() {
44
+ const configPath = getConfigPath();
45
+
46
+ if (!fs.existsSync(configPath)) {
47
+ try {
48
+ fs.writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
49
+ console.log(`✅ Created config file at: ${configPath}`);
50
+ console.log('📝 Please edit this file to set your API key and other preferences.');
51
+ } catch (error) {
52
+ console.warn(`⚠️ Could not create config file at ${configPath}: ${error.message}`);
53
+ }
54
+ } else {
55
+ console.log(`📁 Config file already exists at: ${configPath}`);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Detect if running in GitHub Actions and extract version information
61
+ */
62
+ function detectGitHubActionsContext() {
63
+ // Only detect if running in GitHub Actions environment
64
+ if (!process.env.GITHUB_ACTIONS) {
65
+ return {};
66
+ }
67
+
68
+ const context = {};
69
+
70
+ // Detect version based on GitHub event type
71
+ const eventName = process.env.GITHUB_EVENT_NAME;
72
+ const ref = process.env.GITHUB_REF; // e.g., refs/pull/17/merge, refs/heads/main
73
+
74
+ if (eventName === 'pull_request' && ref) {
75
+ // Extract PR number from ref like "refs/pull/17/merge"
76
+ const prMatch = ref.match(/refs\/pull\/(\d+)\//);
77
+ if (prMatch && prMatch[1]) {
78
+ context.version = `pr-${prMatch[1]}`;
79
+ }
80
+ } else if (ref) {
81
+ // For other events, extract branch name
82
+ if (ref.startsWith('refs/heads/')) {
83
+ const branch = ref.replace('refs/heads/', '');
84
+ // Use branch name, sanitize it for URL safety
85
+ context.version = branch.replace(/[^a-zA-Z0-9-_]/g, '-');
86
+ } else if (ref.startsWith('refs/tags/')) {
87
+ const tag = ref.replace('refs/tags/', '');
88
+ context.version = tag.replace(/[^a-zA-Z0-9-_.]/g, '-');
89
+ }
90
+ }
91
+
92
+ // If we couldn't determine version from ref, use commit SHA
93
+ if (!context.version && process.env.GITHUB_SHA) {
94
+ context.version = process.env.GITHUB_SHA.substring(0, 7);
95
+ }
96
+
97
+ return context;
98
+ }
99
+
100
+ /**
101
+ * Load environment variables with SCRY_ or STORYBOOK_DEPLOYER_ prefix
102
+ */
103
+ function loadEnvConfig() {
104
+ const envConfig = {};
105
+
106
+ // Map environment variable names to config keys
107
+ const envMapping = {
108
+ 'API_URL': 'apiUrl',
109
+ 'VERBOSE': 'verbose',
110
+ 'API_KEY': 'apiKey',
111
+ 'DIR': 'dir',
112
+ 'PROJECT': 'project',
113
+ 'VERSION': 'version',
114
+ 'WITH_ANALYSIS': 'withAnalysis',
115
+ 'STORIES_DIR': 'storiesDir',
116
+ 'SCREENSHOTS_DIR': 'screenshotsDir',
117
+ 'STORYBOOK_URL': 'storybookUrl'
118
+ };
119
+
120
+ Object.keys(envMapping).forEach(envKey => {
121
+ const configKey = envMapping[envKey];
122
+
123
+ // Check SCRY_ prefix first (new standard), then fall back to STORYBOOK_DEPLOYER_ for backward compatibility
124
+ const scryEnvKey = 'SCRY_' + envKey;
125
+ const legacyEnvKey = 'STORYBOOK_DEPLOYER_' + envKey;
126
+
127
+ // Handle special case for PROJECT_ID vs PROJECT
128
+ let envValue = process.env[scryEnvKey];
129
+ if (!envValue && envKey === 'PROJECT') {
130
+ envValue = process.env['SCRY_PROJECT_ID'];
131
+ }
132
+ if (!envValue) {
133
+ envValue = process.env[legacyEnvKey];
134
+ }
135
+
136
+ // Filter out undefined and empty string values to prevent overriding CLI args
137
+ if (envValue !== undefined && envValue !== '') {
138
+ // Convert string values to appropriate types
139
+ if (configKey === 'verbose' || configKey === 'withAnalysis') {
140
+ envConfig[configKey] = envValue.toLowerCase() === 'true';
141
+ } else {
142
+ envConfig[configKey] = envValue;
143
+ }
144
+ }
145
+ });
146
+
147
+ return envConfig;
148
+ }
149
+
150
+ /**
151
+ * Load configuration from file, merging with provided options
152
+ */
153
+ function loadConfig(cliArgs = {}) {
154
+ const configPath = getConfigPath();
155
+ let fileConfig = {};
156
+
157
+ if (fs.existsSync(configPath)) {
158
+ try {
159
+ const configContent = fs.readFileSync(configPath, 'utf8');
160
+ fileConfig = JSON.parse(configContent);
161
+ } catch (error) {
162
+ console.warn(`⚠️ Could not read config file at ${configPath}: ${error.message}`);
163
+ }
164
+ }
165
+
166
+ // Load environment variables
167
+ const envConfig = loadEnvConfig();
168
+
169
+ // Detect GitHub Actions context
170
+ const githubContext = detectGitHubActionsContext();
171
+
172
+ // Log configuration sources for debugging
173
+ console.log('[CONFIG] Configuration sources:');
174
+ console.log(`[CONFIG] - Default apiUrl: ${DEFAULT_CONFIG.apiUrl}`);
175
+ console.log(`[CONFIG] - File config apiUrl: ${fileConfig.apiUrl || 'not set'}`);
176
+ console.log(`[CONFIG] - Env SCRY_API_URL: ${process.env.SCRY_API_URL || 'not set'}`);
177
+ console.log(`[CONFIG] - Env STORYBOOK_DEPLOYER_API_URL: ${process.env.STORYBOOK_DEPLOYER_API_URL || 'not set'}`);
178
+ console.log(`[CONFIG] - CLI args apiUrl: ${cliArgs.apiUrl || 'not set'}`);
179
+
180
+ // Precedence: CLI arguments > GitHub Actions context > environment variables > file config > defaults
181
+ // This ensures GitHub Actions auto-detection works even if env vars are set to empty strings
182
+
183
+ // Map 'deployVersion' (from yargs --deploy-version) to 'version' for internal use
184
+ const normalizedCliArgs = { ...cliArgs };
185
+ if (normalizedCliArgs.deployVersion) {
186
+ normalizedCliArgs.version = normalizedCliArgs.deployVersion;
187
+ delete normalizedCliArgs.deployVersion;
188
+ }
189
+
190
+ const finalConfig = {
191
+ ...DEFAULT_CONFIG,
192
+ ...fileConfig,
193
+ ...envConfig,
194
+ ...githubContext,
195
+ ...normalizedCliArgs
196
+ };
197
+
198
+ console.log(`[CONFIG] Final apiUrl: ${finalConfig.apiUrl}`);
199
+ console.log(`[CONFIG] Final project: ${finalConfig.project}`);
200
+ console.log(`[CONFIG] Final version: ${finalConfig.version}`);
201
+
202
+ return finalConfig;
203
+ }
204
+
205
+ /**
206
+ * Get the path to the config file (for user reference)
207
+ */
208
+ function getConfigFilePath() {
209
+ return getConfigPath();
210
+ }
211
+
212
+ module.exports = {
213
+ createDefaultConfig,
214
+ loadConfig,
215
+ getConfigFilePath,
216
+ DEFAULT_CONFIG
217
+ };
package/lib/errors.js ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Base class for all custom application errors.
3
+ */
4
+ class AppError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = this.constructor.name;
8
+ Error.captureStackTrace(this, this.constructor);
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Error for issues related to file system operations (e.g., zipping).
14
+ */
15
+ class FileSystemError extends AppError {
16
+ constructor(message) {
17
+ super(message);
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Error for failures in API communication.
23
+ */
24
+ class ApiError extends AppError {
25
+ /**
26
+ * @param {string} message The error message.
27
+ * @param {number} [statusCode] The HTTP status code of the response.
28
+ */
29
+ constructor(message, statusCode) {
30
+ super(message);
31
+ this.statusCode = statusCode;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Error for failures during the file upload process to cloud storage.
37
+ */
38
+ class UploadError extends AppError {
39
+ constructor(message) {
40
+ super(message);
41
+ }
42
+ }
43
+
44
+ module.exports = {
45
+ AppError,
46
+ FileSystemError,
47
+ ApiError,
48
+ UploadError,
49
+ };