storemeta 0.1.0
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.
- package/.env.release.example +8 -0
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/auth/apple/credential-state.js +14 -0
- package/dist/auth/apple/jwt.js +56 -0
- package/dist/auth/apple/load-credentials.js +36 -0
- package/dist/auth/credential-state.js +6 -0
- package/dist/auth/google/credential-state.js +10 -0
- package/dist/auth/google/load-credentials.js +18 -0
- package/dist/auth/google/service-account-auth.js +25 -0
- package/dist/auth/redact.js +17 -0
- package/dist/cli/auth-check.js +46 -0
- package/dist/cli/config-doctor.js +40 -0
- package/dist/cli/context.js +23 -0
- package/dist/cli/errors.js +10 -0
- package/dist/cli/exit-code.js +6 -0
- package/dist/cli/init.js +60 -0
- package/dist/cli/locales-list.js +37 -0
- package/dist/cli/metadata-diff.js +57 -0
- package/dist/cli/metadata-pull.js +52 -0
- package/dist/cli/metadata-push.js +95 -0
- package/dist/cli/render.js +57 -0
- package/dist/cli/result-types.js +1 -0
- package/dist/cli/scaffold.js +76 -0
- package/dist/cli/screenshots-diff.js +65 -0
- package/dist/cli/screenshots-pull.js +72 -0
- package/dist/cli/screenshots-push.js +96 -0
- package/dist/cli/validate.js +74 -0
- package/dist/cli.js +222 -0
- package/dist/config/apps.js +21 -0
- package/dist/config/load-config.js +30 -0
- package/dist/config/schema.js +97 -0
- package/dist/config/select-app.js +10 -0
- package/dist/config/select-platforms.js +17 -0
- package/dist/config/single-app.js +16 -0
- package/dist/config/types.js +1 -0
- package/dist/config/validate-base-dirs.js +33 -0
- package/dist/config/validate-credential-env-names.js +32 -0
- package/dist/config/validate-platform-identifiers.js +24 -0
- package/dist/formats/load-metadata.js +38 -0
- package/dist/formats/metadata-types.js +1 -0
- package/dist/formats/screenshot-types.js +1 -0
- package/dist/formats/serialize-metadata.js +20 -0
- package/dist/locales/groups.js +12 -0
- package/dist/locales/map.js +15 -0
- package/dist/locales/normalize.js +22 -0
- package/dist/locales/types.js +1 -0
- package/dist/locales/validate-groups.js +14 -0
- package/dist/platforms/apple/client.js +109 -0
- package/dist/platforms/apple/metadata/pull.js +120 -0
- package/dist/platforms/apple/metadata/push.js +272 -0
- package/dist/platforms/apple/metadata/types.js +1 -0
- package/dist/platforms/apple/screenshots/pull.js +160 -0
- package/dist/platforms/apple/screenshots/push.js +418 -0
- package/dist/platforms/google/client.js +42 -0
- package/dist/platforms/google/edits.js +38 -0
- package/dist/platforms/google/metadata/pull.js +23 -0
- package/dist/platforms/google/metadata/push.js +73 -0
- package/dist/platforms/google/metadata/types.js +1 -0
- package/dist/platforms/google/screenshots/pull.js +129 -0
- package/dist/platforms/google/screenshots/push.js +252 -0
- package/dist/platforms/google/screenshots/types.js +7 -0
- package/dist/validation/metadata/apple.js +31 -0
- package/dist/validation/metadata/files.js +58 -0
- package/dist/validation/metadata/google.js +46 -0
- package/dist/validation/screenshots/extensions.js +17 -0
- package/dist/validation/screenshots/files.js +55 -0
- package/dist/validation/screenshots/folders.js +41 -0
- package/dist/validation/screenshots/order.js +25 -0
- package/dist/writers/ensure-directory.js +16 -0
- package/dist/writers/order-screenshots.js +19 -0
- package/dist/writers/resolve-screenshot-path.js +8 -0
- package/dist/writers/resolve-within-base-dir.js +17 -0
- package/dist/writers/write-metadata.js +17 -0
- package/docs/API_EDITABLE_METADATA_SURFACE.md +319 -0
- package/docs/AUTH_SETUP.md +67 -0
- package/docs/DOCUMENTATION.md +285 -0
- package/docs/PROJECT_PLAN.md +687 -0
- package/docs/RELEASE_VERIFICATION.md +70 -0
- package/docs/TODO.md +308 -0
- package/examples/README.md +8 -0
- package/examples/metadata/apple/en-US.yml +11 -0
- package/examples/metadata/apple/tr.yml +11 -0
- package/examples/metadata/google/en-US.yml +6 -0
- package/examples/metadata/google/tr.yml +6 -0
- package/examples/screenshots/apple/en-US/APP_IPHONE_65/1.png +1 -0
- package/examples/screenshots/apple/tr/APP_IPHONE_65/1.png +1 -0
- package/examples/screenshots/google/en-US/phoneScreenshots/1.png +1 -0
- package/examples/screenshots/google/tr/phoneScreenshots/1.png +1 -0
- package/examples/storemeta.yml +29 -0
- package/package.json +55 -0
- package/storemeta.release.example.yml +27 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { StoremetaError } from "../cli/errors.js";
|
|
4
|
+
export async function ensureDirectory(directoryPath) {
|
|
5
|
+
const resolvedPath = resolve(directoryPath);
|
|
6
|
+
try {
|
|
7
|
+
await mkdir(resolvedPath, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
catch (cause) {
|
|
10
|
+
throw new StoremetaError("FILESYSTEM_ERROR", `Failed to create directory at ${resolvedPath}`, { cause });
|
|
11
|
+
}
|
|
12
|
+
return resolvedPath;
|
|
13
|
+
}
|
|
14
|
+
export async function ensureParentDirectory(filePath) {
|
|
15
|
+
return ensureDirectory(dirname(resolve(filePath)));
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
function getCanonicalScreenshotExtension(fileName) {
|
|
3
|
+
const extension = extname(fileName).toLowerCase();
|
|
4
|
+
return extension.length > 0 ? extension : ".png";
|
|
5
|
+
}
|
|
6
|
+
export function orderScreenshotsForWrite(screenshots) {
|
|
7
|
+
return [...screenshots]
|
|
8
|
+
.sort((left, right) => {
|
|
9
|
+
if (left.position !== right.position) {
|
|
10
|
+
return left.position - right.position;
|
|
11
|
+
}
|
|
12
|
+
return left.fileName.localeCompare(right.fileName);
|
|
13
|
+
})
|
|
14
|
+
.map((screenshot, index) => ({
|
|
15
|
+
...screenshot,
|
|
16
|
+
position: index + 1,
|
|
17
|
+
fileName: `${index + 1}${getCanonicalScreenshotExtension(screenshot.fileName)}`,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { normalizeLocaleCode } from "../locales/normalize.js";
|
|
2
|
+
import { resolvePathWithinBaseDir } from "./resolve-within-base-dir.js";
|
|
3
|
+
export function resolveScreenshotSetDirectory(baseDir, screenshotSet) {
|
|
4
|
+
return resolvePathWithinBaseDir(baseDir, screenshotSet.platform, normalizeLocaleCode(screenshotSet.locale), screenshotSet.assetType);
|
|
5
|
+
}
|
|
6
|
+
export function resolveScreenshotFilePath(baseDir, screenshot) {
|
|
7
|
+
return resolvePathWithinBaseDir(baseDir, screenshot.platform, normalizeLocaleCode(screenshot.locale), screenshot.assetType, screenshot.fileName);
|
|
8
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
|
+
import { StoremetaError } from "../cli/errors.js";
|
|
3
|
+
export function assertPathWithinBaseDir(baseDir, targetPath) {
|
|
4
|
+
const resolvedBaseDir = resolve(baseDir);
|
|
5
|
+
const resolvedTargetPath = resolve(targetPath);
|
|
6
|
+
const relativePath = relative(resolvedBaseDir, resolvedTargetPath);
|
|
7
|
+
if (relativePath === "" ||
|
|
8
|
+
(!relativePath.startsWith("..") && !isAbsolute(relativePath))) {
|
|
9
|
+
return resolvedTargetPath;
|
|
10
|
+
}
|
|
11
|
+
throw new StoremetaError("FILESYSTEM_ERROR", `Resolved path escapes base directory: ${resolvedTargetPath}`);
|
|
12
|
+
}
|
|
13
|
+
export function resolvePathWithinBaseDir(baseDir, ...pathSegments) {
|
|
14
|
+
const resolvedBaseDir = resolve(baseDir);
|
|
15
|
+
const resolvedTargetPath = resolve(resolvedBaseDir, ...pathSegments);
|
|
16
|
+
return assertPathWithinBaseDir(resolvedBaseDir, resolvedTargetPath);
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { StoremetaError } from "../cli/errors.js";
|
|
3
|
+
import { serializeMetadataDocument } from "../formats/serialize-metadata.js";
|
|
4
|
+
import { ensureParentDirectory } from "./ensure-directory.js";
|
|
5
|
+
import { resolvePathWithinBaseDir } from "./resolve-within-base-dir.js";
|
|
6
|
+
export async function writeMetadataFile(baseDir, filePath, document) {
|
|
7
|
+
const resolvedPath = resolvePathWithinBaseDir(baseDir, filePath);
|
|
8
|
+
const serialized = serializeMetadataDocument(document);
|
|
9
|
+
try {
|
|
10
|
+
await ensureParentDirectory(resolvedPath);
|
|
11
|
+
await writeFile(resolvedPath, serialized, "utf8");
|
|
12
|
+
}
|
|
13
|
+
catch (cause) {
|
|
14
|
+
throw new StoremetaError("FILESYSTEM_ERROR", `Failed to write metadata file at ${resolvedPath}`, { cause });
|
|
15
|
+
}
|
|
16
|
+
return resolvedPath;
|
|
17
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# API Editable Metadata Surface
|
|
2
|
+
|
|
3
|
+
This document lists the app metadata and store-asset areas that are editable through the public Apple and Google APIs, based on the official documentation reviewed on March 27, 2026.
|
|
4
|
+
|
|
5
|
+
Scope:
|
|
6
|
+
- app/store metadata and assets
|
|
7
|
+
- localized and non-localized surfaces
|
|
8
|
+
- public API-editable items only
|
|
9
|
+
|
|
10
|
+
Out of scope:
|
|
11
|
+
- binaries and track rollout mechanics except where directly tied to metadata
|
|
12
|
+
- in-app purchases and subscriptions
|
|
13
|
+
- account administration
|
|
14
|
+
- analytics and reporting
|
|
15
|
+
|
|
16
|
+
This document is intentionally conservative:
|
|
17
|
+
- `Confirmed` means the official docs reviewed clearly expose a writable public API path or request body.
|
|
18
|
+
- `Likely` means the reviewed docs strongly imply API support, but the exact writable field list was not fully visible from the sources reviewed.
|
|
19
|
+
- `Not confirmed` means the field exists in product/help docs, but public writable API support was not confirmed from the reviewed sources.
|
|
20
|
+
|
|
21
|
+
## Apple
|
|
22
|
+
|
|
23
|
+
### Localizable And Confirmed API-Editable
|
|
24
|
+
|
|
25
|
+
These are confirmed as part of the App Store Connect API metadata surface.
|
|
26
|
+
|
|
27
|
+
#### App info localization surface
|
|
28
|
+
|
|
29
|
+
- `name`
|
|
30
|
+
- `subtitle`
|
|
31
|
+
|
|
32
|
+
Resource model:
|
|
33
|
+
- `appInfoLocalizations`
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
- These are shared app-info fields localized per locale.
|
|
37
|
+
|
|
38
|
+
#### App version localization surface
|
|
39
|
+
|
|
40
|
+
- `description`
|
|
41
|
+
- `keywords`
|
|
42
|
+
- `marketingUrl`
|
|
43
|
+
- `promotionalText`
|
|
44
|
+
- `supportUrl`
|
|
45
|
+
- `whatsNew`
|
|
46
|
+
|
|
47
|
+
Resource model:
|
|
48
|
+
- `appStoreVersionLocalizations`
|
|
49
|
+
|
|
50
|
+
Notes:
|
|
51
|
+
- These are localized per app version and locale.
|
|
52
|
+
- `whatsNew` is specifically version-oriented.
|
|
53
|
+
|
|
54
|
+
#### Localized visual assets
|
|
55
|
+
|
|
56
|
+
- screenshots
|
|
57
|
+
- app previews
|
|
58
|
+
|
|
59
|
+
Resource model:
|
|
60
|
+
- `appScreenshotSets`
|
|
61
|
+
- `appScreenshots`
|
|
62
|
+
- `appPreviewSets`
|
|
63
|
+
- `appPreviews`
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
- These hang off app store version localizations.
|
|
67
|
+
- Screenshots are clearly part of the localized product-page surface.
|
|
68
|
+
- App previews are API-editable and are attached through preview sets.
|
|
69
|
+
|
|
70
|
+
### Non-Localized And Confirmed API-Editable
|
|
71
|
+
|
|
72
|
+
#### App store version surface
|
|
73
|
+
|
|
74
|
+
- `versionString`
|
|
75
|
+
- `copyright`
|
|
76
|
+
- `releaseType`
|
|
77
|
+
- `earliestReleaseDate`
|
|
78
|
+
|
|
79
|
+
Resource model:
|
|
80
|
+
- `appStoreVersions`
|
|
81
|
+
|
|
82
|
+
Notes:
|
|
83
|
+
- These are version-scoped rather than localization-scoped.
|
|
84
|
+
|
|
85
|
+
#### Build linkage
|
|
86
|
+
|
|
87
|
+
- attached build for a version
|
|
88
|
+
|
|
89
|
+
Resource model:
|
|
90
|
+
- `appStoreVersions/{id}/relationships/build`
|
|
91
|
+
|
|
92
|
+
Notes:
|
|
93
|
+
- This is not a textual metadata field, but it is part of the version configuration surface.
|
|
94
|
+
|
|
95
|
+
#### App review information
|
|
96
|
+
|
|
97
|
+
- `contactFirstName`
|
|
98
|
+
- `contactLastName`
|
|
99
|
+
- `contactPhone`
|
|
100
|
+
- `contactEmail`
|
|
101
|
+
- `demoAccountName`
|
|
102
|
+
- `demoAccountPassword`
|
|
103
|
+
- `demoAccountRequired`
|
|
104
|
+
- `notes`
|
|
105
|
+
|
|
106
|
+
Resource model:
|
|
107
|
+
- `appStoreReviewDetails`
|
|
108
|
+
|
|
109
|
+
#### Age rating declaration
|
|
110
|
+
|
|
111
|
+
Confirmed API-addressable fields include:
|
|
112
|
+
- `advertising`
|
|
113
|
+
- `alcoholTobaccoOrDrugUseOrReferences`
|
|
114
|
+
- `contests`
|
|
115
|
+
- `gambling`
|
|
116
|
+
- `gamblingSimulated`
|
|
117
|
+
- `gunsOrOtherWeapons`
|
|
118
|
+
- `healthOrWellnessTopics`
|
|
119
|
+
- `kidsAgeBand`
|
|
120
|
+
- `lootBox`
|
|
121
|
+
- `medicalOrTreatmentInformation`
|
|
122
|
+
- `messagingAndChat`
|
|
123
|
+
- `parentalControls`
|
|
124
|
+
- `profanityOrCrudeHumor`
|
|
125
|
+
- `ageAssurance`
|
|
126
|
+
- `sexualContentGraphicAndNudity`
|
|
127
|
+
- `sexualContentOrNudity`
|
|
128
|
+
- `horrorOrFearThemes`
|
|
129
|
+
- `matureOrSuggestiveThemes`
|
|
130
|
+
- `unrestrictedWebAccess`
|
|
131
|
+
- `userGeneratedContent`
|
|
132
|
+
- `violenceCartoonOrFantasy`
|
|
133
|
+
- `violenceRealisticProlongedGraphicOrSadistic`
|
|
134
|
+
- `violenceRealistic`
|
|
135
|
+
- `ageRatingOverride`
|
|
136
|
+
- `ageRatingOverrideV2`
|
|
137
|
+
- `koreaAgeRatingOverride`
|
|
138
|
+
- `developerAgeRatingInfoUrl`
|
|
139
|
+
|
|
140
|
+
Resource model:
|
|
141
|
+
- `ageRatingDeclarations`
|
|
142
|
+
|
|
143
|
+
#### Accessibility declarations
|
|
144
|
+
|
|
145
|
+
Confirmed API-addressable fields include:
|
|
146
|
+
- `deviceFamily`
|
|
147
|
+
- `state`
|
|
148
|
+
- `supportsAudioDescriptions`
|
|
149
|
+
- `supportsCaptions`
|
|
150
|
+
- `supportsDarkInterface`
|
|
151
|
+
- `supportsDifferentiateWithoutColorAlone`
|
|
152
|
+
- `supportsLargerText`
|
|
153
|
+
- `supportsReducedMotion`
|
|
154
|
+
- `supportsSufficientContrast`
|
|
155
|
+
- `supportsVoiceControl`
|
|
156
|
+
- `supportsVoiceover`
|
|
157
|
+
|
|
158
|
+
Resource model:
|
|
159
|
+
- `accessibilityDeclarations`
|
|
160
|
+
|
|
161
|
+
### Likely API-Editable, But Exact Writable Field List Not Fully Confirmed From Reviewed Sources
|
|
162
|
+
|
|
163
|
+
These appear to be API-addressable from the official docs reviewed, but the exact writable field list was not fully visible in the source snippets reviewed.
|
|
164
|
+
|
|
165
|
+
- primary category
|
|
166
|
+
- primary subcategories
|
|
167
|
+
- secondary category
|
|
168
|
+
- secondary subcategories
|
|
169
|
+
|
|
170
|
+
Resource hints:
|
|
171
|
+
- `appInfos`
|
|
172
|
+
- `appCategories`
|
|
173
|
+
|
|
174
|
+
Reason for caution:
|
|
175
|
+
- Apple’s category docs explicitly say to update categories through the `App Infos` resource, but the exact request schema was not fully visible in the reviewed sources.
|
|
176
|
+
|
|
177
|
+
### Not Confirmed From Reviewed Sources
|
|
178
|
+
|
|
179
|
+
These exist in Apple’s product/help documentation, but writable public API support was not confirmed from the reviewed sources.
|
|
180
|
+
|
|
181
|
+
- `privacyPolicyUrl`
|
|
182
|
+
- `privacyChoicesUrl`
|
|
183
|
+
- custom license agreement text
|
|
184
|
+
- app privacy questionnaire data types
|
|
185
|
+
|
|
186
|
+
Reason for caution:
|
|
187
|
+
- They may be writable through other public endpoints, but that was not confirmed from the sources reviewed for this document.
|
|
188
|
+
|
|
189
|
+
## Google Play
|
|
190
|
+
|
|
191
|
+
### Localizable And Confirmed API-Editable
|
|
192
|
+
|
|
193
|
+
#### Listing text
|
|
194
|
+
|
|
195
|
+
- `title`
|
|
196
|
+
- `shortDescription`
|
|
197
|
+
- `fullDescription`
|
|
198
|
+
- `video`
|
|
199
|
+
|
|
200
|
+
Resource model:
|
|
201
|
+
- `edits.listings`
|
|
202
|
+
|
|
203
|
+
Notes:
|
|
204
|
+
- The listing resource is locale-specific.
|
|
205
|
+
- `video` is the promotional YouTube URL in the listing resource.
|
|
206
|
+
|
|
207
|
+
#### Localized graphic assets
|
|
208
|
+
|
|
209
|
+
- `phoneScreenshots`
|
|
210
|
+
- `sevenInchScreenshots`
|
|
211
|
+
- `tenInchScreenshots`
|
|
212
|
+
- `tvScreenshots`
|
|
213
|
+
- `wearScreenshots`
|
|
214
|
+
- `icon`
|
|
215
|
+
- `featureGraphic`
|
|
216
|
+
- `tvBanner`
|
|
217
|
+
|
|
218
|
+
Resource model:
|
|
219
|
+
- `edits.images`
|
|
220
|
+
- `AppImageType`
|
|
221
|
+
|
|
222
|
+
Notes:
|
|
223
|
+
- The images API is language-scoped.
|
|
224
|
+
- This means screenshots and the listed graphic asset types are part of the localized editable API surface.
|
|
225
|
+
|
|
226
|
+
### Non-Localized And Confirmed API-Editable
|
|
227
|
+
|
|
228
|
+
- `defaultLanguage`
|
|
229
|
+
- `contactWebsite`
|
|
230
|
+
- `contactEmail`
|
|
231
|
+
- `contactPhone`
|
|
232
|
+
|
|
233
|
+
Resource model:
|
|
234
|
+
- `edits.details`
|
|
235
|
+
|
|
236
|
+
Notes:
|
|
237
|
+
- These are app-level details rather than locale-specific listing text.
|
|
238
|
+
|
|
239
|
+
### Explicitly Not Supported Or Not Fully Editable Through The Publishing API
|
|
240
|
+
|
|
241
|
+
The official Google docs explicitly note some limits.
|
|
242
|
+
|
|
243
|
+
Notable examples:
|
|
244
|
+
- legal consents required for publishing are not fillable through the API
|
|
245
|
+
- changing app state from published to unpublished is not supported through this API
|
|
246
|
+
|
|
247
|
+
## Practical V1 Implications For `storemeta`
|
|
248
|
+
|
|
249
|
+
### Confirmed API Surface Worth Targeting
|
|
250
|
+
|
|
251
|
+
Apple localized:
|
|
252
|
+
- `name`
|
|
253
|
+
- `subtitle`
|
|
254
|
+
- `description`
|
|
255
|
+
- `keywords`
|
|
256
|
+
- `marketingUrl`
|
|
257
|
+
- `promotionalText`
|
|
258
|
+
- `supportUrl`
|
|
259
|
+
- `whatsNew`
|
|
260
|
+
- screenshots
|
|
261
|
+
- app previews
|
|
262
|
+
|
|
263
|
+
Apple non-localized:
|
|
264
|
+
- `versionString`
|
|
265
|
+
- `copyright`
|
|
266
|
+
- app review details
|
|
267
|
+
|
|
268
|
+
Google localized:
|
|
269
|
+
- `title`
|
|
270
|
+
- `shortDescription`
|
|
271
|
+
- `fullDescription`
|
|
272
|
+
- `video`
|
|
273
|
+
- screenshots
|
|
274
|
+
- `icon`
|
|
275
|
+
- `featureGraphic`
|
|
276
|
+
- `tvBanner`
|
|
277
|
+
|
|
278
|
+
Google non-localized:
|
|
279
|
+
- `defaultLanguage`
|
|
280
|
+
- `contactWebsite`
|
|
281
|
+
- `contactEmail`
|
|
282
|
+
- `contactPhone`
|
|
283
|
+
|
|
284
|
+
### Current CLI Implementation Cut
|
|
285
|
+
|
|
286
|
+
The current `storemeta` CLI implements this subset:
|
|
287
|
+
|
|
288
|
+
Apple:
|
|
289
|
+
- app-info localization text
|
|
290
|
+
- app-version localization text
|
|
291
|
+
- screenshots
|
|
292
|
+
|
|
293
|
+
Google:
|
|
294
|
+
- listing text
|
|
295
|
+
- screenshots
|
|
296
|
+
|
|
297
|
+
Everything beyond that should be implemented only after confirming the exact write semantics in code and tests.
|
|
298
|
+
|
|
299
|
+
## Sources
|
|
300
|
+
|
|
301
|
+
Apple:
|
|
302
|
+
- App information help: https://developer.apple.com/help/app-store-connect/reference/app-information/app-information
|
|
303
|
+
- Required, localizable, and editable properties: https://developer.apple.com/help/app-store-connect/reference/app-information/required-localizable-and-editable-properties
|
|
304
|
+
- App Store localizations: https://developer.apple.com/help/app-store-connect/reference/app-information/app-store-localizations
|
|
305
|
+
- App Store version localizations fields: https://developer.apple.com/documentation/appstoreconnectapi/appstoreversionlocalizationcreaterequest/data-data.dictionary/attributes-data.dictionary
|
|
306
|
+
- App Store review detail fields: https://developer.apple.com/documentation/appstoreconnectapi/appstorereviewdetail/attributes-data.dictionary
|
|
307
|
+
- App Store versions fields and related resources: https://developer.apple.com/documentation/appstoreconnectapi/get-v1-apps-_id_-appstoreversions
|
|
308
|
+
- App previews modify endpoint: https://developer.apple.com/documentation/appstoreconnectapi/patch-v1-apppreviews-_id_
|
|
309
|
+
- Build linkage endpoint: https://developer.apple.com/documentation/appstoreconnectapi/patch-v1-appstoreversions-_id_-relationships-build
|
|
310
|
+
- Categories overview: https://developer.apple.com/documentation/appstoreconnectapi/app-categories
|
|
311
|
+
- Accessibility declarations fields: https://developer.apple.com/documentation/appstoreconnectapi/get-v1-apps-_id_-accessibilitydeclarations
|
|
312
|
+
- Age rating modify endpoint: https://developer.apple.com/documentation/appstoreconnectapi/patch-v1-ageratingdeclarations-_id_
|
|
313
|
+
|
|
314
|
+
Google:
|
|
315
|
+
- Edits overview: https://developers.google.com/android-publisher/edits
|
|
316
|
+
- Listings resource: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.listings
|
|
317
|
+
- Images resource: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.images
|
|
318
|
+
- App image types: https://developers.google.com/android-publisher/api-ref/rest/v3/AppImageType
|
|
319
|
+
- App details resource: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.details
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Auth Setup
|
|
2
|
+
|
|
3
|
+
`storemeta` reads credentials from environment variables. Do not put secret values directly in `storemeta.yml`.
|
|
4
|
+
|
|
5
|
+
## Apple App Store Connect
|
|
6
|
+
|
|
7
|
+
Create an API key:
|
|
8
|
+
|
|
9
|
+
1. Open App Store Connect.
|
|
10
|
+
2. Go to Users and Access.
|
|
11
|
+
3. Open Integrations.
|
|
12
|
+
4. Open App Store Connect API.
|
|
13
|
+
5. Create or select an API key with access to the target app.
|
|
14
|
+
6. Copy the Issuer ID.
|
|
15
|
+
7. Copy the Key ID.
|
|
16
|
+
8. Download the `.p8` private key file.
|
|
17
|
+
|
|
18
|
+
Configure your shell:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
export STORE_APPLE_ISSUER_ID=00000000-0000-0000-0000-000000000000
|
|
22
|
+
export STORE_APPLE_KEY_ID=XXXXXXXXXX
|
|
23
|
+
export STORE_APPLE_PRIVATE_KEY_PATH=/absolute/path/to/AuthKey_XXXXXXXXXX.p8
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Common Apple errors:
|
|
27
|
+
|
|
28
|
+
- `401 Unauthorized`: issuer id, key id, and `.p8` file do not match, or the key does not have access to the app.
|
|
29
|
+
- Missing credential env var: the environment variable name in `storemeta.yml` is not exported in the current shell.
|
|
30
|
+
- File read failure: `STORE_APPLE_PRIVATE_KEY_PATH` does not point to a readable `.p8` file.
|
|
31
|
+
|
|
32
|
+
## Google Play
|
|
33
|
+
|
|
34
|
+
Create a service account:
|
|
35
|
+
|
|
36
|
+
1. Open Google Cloud Console.
|
|
37
|
+
2. Create or select a project.
|
|
38
|
+
3. Enable the Google Play Developer API.
|
|
39
|
+
4. Create a service account.
|
|
40
|
+
5. Create and download a JSON key for that service account.
|
|
41
|
+
6. Open Google Play Console.
|
|
42
|
+
7. Go to Users and permissions.
|
|
43
|
+
8. Invite the service account email as a user.
|
|
44
|
+
9. Grant access to the target app and the permissions needed for metadata and screenshots.
|
|
45
|
+
|
|
46
|
+
Configure your shell:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
export STORE_GOOGLE_SERVICE_ACCOUNT_PATH=/absolute/path/to/google-service-account.json
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Common Google errors:
|
|
53
|
+
|
|
54
|
+
- `401 Unauthorized`: service account JSON is invalid or cannot exchange an OAuth token.
|
|
55
|
+
- `403 Forbidden`: service account is not added to Play Console or lacks permission for the app.
|
|
56
|
+
- `400 Bad Request` for locale: Google Play expects locale codes such as `tr-TR`, not just `tr`.
|
|
57
|
+
|
|
58
|
+
## Check Credentials
|
|
59
|
+
|
|
60
|
+
Run:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
storemeta auth check
|
|
64
|
+
storemeta validate
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`auth check` confirms that configured credential environment variables are present. `validate` checks config, metadata files, and screenshot layout.
|