semantic-release-steam 2.1.2 → 2.1.4
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/README.md +24 -229
- package/index.mjs +47 -28
- package/lib/description.mjs +1 -1
- package/lib/readme.mjs +14 -22
- package/lib/vdf.mjs +3 -5
- package/package.json +9 -10
- package/schema/plugin-config.json +1 -1
package/README.md
CHANGED
|
@@ -1,69 +1,33 @@
|
|
|
1
1
|
# semantic-release-steam
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Originally extracted from the [RimworldCosmere](https://github.com/RimworldCosmere/RimworldCosmere) project's release pipeline. Generic enough to publish any Steam Workshop item for any Steam app, not just RimWorld.
|
|
6
|
-
|
|
7
|
-
## What it does
|
|
8
|
-
|
|
9
|
-
On `verifyConditions`:
|
|
10
|
-
- Validates that the current branch maps to a configured workshop target (e.g. `main` -> `stable`, `beta` -> `beta`)
|
|
11
|
-
- Checks that `STEAM_USERNAME`, a Steam `config.vdf` source (`STEAM_CONFIG_VDF` path or `STEAM_CONFIG_VDF_B64` contents), and `appId` are present
|
|
12
|
-
- Throws clearly if a mod has no `workshopIds`, if a target key looks misspelled (with a "did you mean" suggestion), or if no mod is publishable for the resolved target
|
|
13
|
-
|
|
14
|
-
On `publish`:
|
|
15
|
-
- Compiles a Steam Workshop description from each mod's `README.template.md` (markdown -> BBCode via [`@steamdown/core`](https://www.npmjs.com/package/@steamdown/core))
|
|
16
|
-
- Stages mod content into a temp dir, respecting `.steamignore`
|
|
17
|
-
- Generates a `workshop.vdf` with `appid`, `publishedfileid`, `contentfolder`, `description`, `changenote`, plus optional `title`, `previewfile`, `visibility`, and `tags`
|
|
18
|
-
- Invokes `steamcmd +login $STEAM_USERNAME +workshop_build_item ... +quit`
|
|
19
|
-
- Honors `semantic-release --dry-run`: logs the intended action without invoking SteamCMD
|
|
20
|
-
|
|
21
|
-
The plugin assumes you have already created the workshop item once manually via SteamCMD. It updates existing workshop items by their `publishedfileid` - it does NOT create them. (See "First publish" below.)
|
|
3
|
+
Use [semantic-release](https://semantic-release.gitbook.io/semantic-release/) to publish built mod directories to existing Steam Workshop items. The plugin uploads through [SteamCMD](https://developer.valvesoftware.com/wiki/SteamCMD) and converts each mod's `README.template.md` to Steam BBCode with [`@steamdown/core`](https://www.npmjs.com/package/@steamdown/core).
|
|
22
4
|
|
|
23
5
|
## Install
|
|
24
6
|
|
|
25
7
|
```bash
|
|
26
|
-
npm install -
|
|
8
|
+
npm install --save-dev semantic-release-steam
|
|
27
9
|
```
|
|
28
10
|
|
|
29
|
-
|
|
30
|
-
- [SteamCMD](https://developer.valvesoftware.com/wiki/SteamCMD) on `PATH` (or set `STEAMCMD_PATH`). On GitHub Actions, [`buildalon/setup-steamcmd@v1`](https://github.com/marketplace/actions/setup-steamcmd) handles this.
|
|
31
|
-
- `rsync` (preinstalled on `ubuntu-latest` runners and macOS).
|
|
32
|
-
- A pre-authenticated `config.vdf` from SteamCMD (login once interactively, then base64-encode the resulting file).
|
|
33
|
-
|
|
34
|
-
> [!NOTE]
|
|
35
|
-
> As of v2, the markdown-to-BBCode conversion is bundled as a library dependency. You no longer need to `npm install -g @steamdown/cli`.
|
|
11
|
+
Your release runner needs Node.js 20+, SteamCMD, and `rsync`.
|
|
36
12
|
|
|
37
|
-
##
|
|
13
|
+
## Configure
|
|
38
14
|
|
|
39
|
-
|
|
15
|
+
Add the plugin to `release.config.mjs`:
|
|
40
16
|
|
|
41
17
|
```js
|
|
42
18
|
export default {
|
|
43
|
-
branches: ['main'
|
|
19
|
+
branches: ['main'],
|
|
44
20
|
plugins: [
|
|
45
|
-
'@semantic-release/commit-analyzer',
|
|
46
|
-
'@semantic-release/release-notes-generator',
|
|
47
21
|
[
|
|
48
22
|
'semantic-release-steam',
|
|
49
23
|
{
|
|
50
24
|
appId: '294100',
|
|
51
|
-
branchTargets: { main: 'stable'
|
|
52
|
-
descriptionHeader: '## My Mod\n\n',
|
|
53
|
-
descriptionFooter: '\n\n---\n\nReport issues on GitHub.',
|
|
54
|
-
assetBaseUrlTemplate: 'https://raw.githubusercontent.com/me/my-mod/{branch}',
|
|
55
|
-
uploadTimeoutMs: 600000,
|
|
25
|
+
branchTargets: { main: 'stable' },
|
|
56
26
|
mods: [
|
|
57
27
|
{
|
|
58
|
-
name: '
|
|
59
|
-
path: 'MyMod',
|
|
60
|
-
workshopIds: { stable: '1234567890'
|
|
61
|
-
title: 'My Mod',
|
|
62
|
-
tags: ['QoL', 'Utility'],
|
|
63
|
-
visibility: 0,
|
|
64
|
-
metadata: {
|
|
65
|
-
beta: { title: '[BETA] My Mod' },
|
|
66
|
-
},
|
|
28
|
+
name: 'My Mod',
|
|
29
|
+
path: 'dist/MyMod',
|
|
30
|
+
workshopIds: { stable: '1234567890' },
|
|
67
31
|
},
|
|
68
32
|
],
|
|
69
33
|
},
|
|
@@ -72,196 +36,27 @@ export default {
|
|
|
72
36
|
};
|
|
73
37
|
```
|
|
74
38
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
| Option | Required | Description |
|
|
78
|
-
|---|---|---|
|
|
79
|
-
| `appId` | Yes | Steam app ID (e.g. `294100` for RimWorld). |
|
|
80
|
-
| `branchTargets` | Yes | Map of git branch name to workshop target key. Branches not in the map are skipped (silently). |
|
|
81
|
-
| `mods` | Yes | Array of [mod config](#mod-config). |
|
|
82
|
-
| `descriptionHeader` | No | String prepended to each mod's compiled README before BBCode conversion. |
|
|
83
|
-
| `descriptionFooter` | No | String appended to each mod's compiled README. |
|
|
84
|
-
| `assetBaseUrlTemplate` | No | URL template with `{branch}` placeholder. Asset paths matching `../.github/assets/` in the README are rewritten to absolute URLs against this base. |
|
|
85
|
-
| `assetDirNameTransform` | No | Either an array of subdirectory names under `.github/assets/` (literals or glob patterns), or a function `(modPath) => string[]` returning the ordered list. Default: `[basename(modPath).toLowerCase(), 'fallback']`. |
|
|
86
|
-
| `outputReadme` | No | If `true`, also write the compiled markdown to `<modPath>/README.md` (v1 behavior). Default `false` — the compiled README stays in memory. |
|
|
87
|
-
| `uploadTimeoutMs` | No | Max time in ms to wait for SteamCMD. Default `600000` (10 min). |
|
|
88
|
-
| `verbose` | No | Log SteamCMD stdout/stderr at info level. Default `false` (debug level). |
|
|
89
|
-
|
|
90
|
-
### Mod config
|
|
91
|
-
|
|
92
|
-
| Field | Required | Description |
|
|
93
|
-
|---|---|---|
|
|
94
|
-
| `name` | Yes | Human-readable name, used in logs. |
|
|
95
|
-
| `path` | Yes | Mod content directory, relative to the semantic-release cwd. |
|
|
96
|
-
| `workshopIds` | Yes | Map of branch-target key (from `branchTargets`) to Steam Workshop `publishedfileid`. |
|
|
97
|
-
| `title` | No | Workshop item title. Only written into the VDF if set. |
|
|
98
|
-
| `previewfile` | No | Path to a preview image (jpg/png). Only written if set. |
|
|
99
|
-
| `visibility` | No | `0` = public, `1` = friends-only, `2` = private, `3` = unlisted. |
|
|
100
|
-
| `tags` | No | Workshop tag array. |
|
|
101
|
-
| `metadata` | No | Per-target overrides. Keys must match values from `branchTargets`. Per-target fields win over mod-level defaults. |
|
|
102
|
-
|
|
103
|
-
Workshop fields (`title`, `previewfile`, `visibility`, `tags`) are only emitted into the VDF when set, so manual edits made in the Steam workshop UI are preserved across publishes for fields you don't manage in config.
|
|
104
|
-
|
|
105
|
-
### Required environment variables
|
|
106
|
-
|
|
107
|
-
| Var | Purpose |
|
|
108
|
-
|---|---|
|
|
109
|
-
| `STEAM_USERNAME` | Steam account username with workshop publish permission. |
|
|
110
|
-
| `STEAM_CONFIG_VDF` | Path to a pre-authenticated `config.vdf`. |
|
|
111
|
-
| `STEAM_CONFIG_VDF_B64` | Alternative to `STEAM_CONFIG_VDF`: base64-encoded contents of `config.vdf`. The plugin decodes it to a temp file. |
|
|
112
|
-
| `STEAMCMD_PATH` | Optional. Path to `steamcmd.sh` / `steamcmd.exe`. Defaults to `~/steamcmd/steamcmd.sh`. |
|
|
113
|
-
|
|
114
|
-
At least one of `STEAM_CONFIG_VDF` or `STEAM_CONFIG_VDF_B64` must be set. If both are set, the path wins.
|
|
115
|
-
|
|
116
|
-
> [!IMPORTANT]
|
|
117
|
-
> SteamCMD reads its auth from `$STEAM_DIR/config/config.vdf` (the install dir's config directory). Setting `STEAM_CONFIG_VDF` only tells the plugin where to find the file; you still need to put a copy at the path SteamCMD expects. See the GitHub Actions example below for the standard pattern.
|
|
118
|
-
|
|
119
|
-
## GitHub Actions example
|
|
120
|
-
|
|
121
|
-
A copy-pasteable workflow that runs semantic-release with the Steam plugin:
|
|
122
|
-
|
|
123
|
-
```yaml
|
|
124
|
-
name: release
|
|
125
|
-
|
|
126
|
-
on:
|
|
127
|
-
push:
|
|
128
|
-
branches: [main, beta]
|
|
129
|
-
|
|
130
|
-
permissions:
|
|
131
|
-
contents: write
|
|
132
|
-
issues: write
|
|
133
|
-
pull-requests: write
|
|
134
|
-
id-token: write
|
|
135
|
-
|
|
136
|
-
jobs:
|
|
137
|
-
release:
|
|
138
|
-
runs-on: ubuntu-latest
|
|
139
|
-
steps:
|
|
140
|
-
- uses: actions/checkout@v4
|
|
141
|
-
with:
|
|
142
|
-
fetch-depth: 0
|
|
143
|
-
persist-credentials: false
|
|
144
|
-
|
|
145
|
-
- uses: actions/setup-node@v4
|
|
146
|
-
with:
|
|
147
|
-
node-version: '22'
|
|
148
|
-
|
|
149
|
-
- name: Install
|
|
150
|
-
run: npm ci
|
|
151
|
-
|
|
152
|
-
- name: Set up SteamCMD
|
|
153
|
-
uses: buildalon/setup-steamcmd@v1
|
|
154
|
-
|
|
155
|
-
- name: Restore Steam config
|
|
156
|
-
env:
|
|
157
|
-
STEAM_CONFIG_VDF_B64: ${{ secrets.STEAM_CONFIG_VDF_B64 }}
|
|
158
|
-
run: |
|
|
159
|
-
mkdir -p "$STEAM_DIR/config"
|
|
160
|
-
printf '%s' "$STEAM_CONFIG_VDF_B64" | base64 -d > "$STEAM_DIR/config/config.vdf"
|
|
161
|
-
echo "STEAM_CONFIG_VDF=$STEAM_DIR/config/config.vdf" >> "$GITHUB_ENV"
|
|
162
|
-
|
|
163
|
-
- name: Steam login preflight
|
|
164
|
-
env:
|
|
165
|
-
STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }}
|
|
166
|
-
run: |
|
|
167
|
-
"$STEAM_CMD/steamcmd.sh" +@ShutdownOnFailedCommand 1 +@NoPromptForPassword 1 +login "$STEAM_USERNAME" +quit
|
|
168
|
-
|
|
169
|
-
- name: Run semantic-release
|
|
170
|
-
env:
|
|
171
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
172
|
-
STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }}
|
|
173
|
-
STEAM_CONFIG_VDF: ${{ env.STEAM_CONFIG_VDF }}
|
|
174
|
-
STEAMCMD_PATH: ${{ env.STEAM_CMD }}/steamcmd.sh
|
|
175
|
-
run: npx semantic-release
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
The two secrets you need:
|
|
179
|
-
- `STEAM_USERNAME` — your Steam account username
|
|
180
|
-
- `STEAM_CONFIG_VDF_B64` — `base64 -w 0 ~/Steam/config/config.vdf` from a machine where you've successfully logged in with `steamcmd +login <user> +quit`
|
|
181
|
-
|
|
182
|
-
## First publish
|
|
183
|
-
|
|
184
|
-
semantic-release-steam UPDATES existing workshop items - it does not create them. Before the plugin can run, each mod needs a `publishedfileid` per branch target.
|
|
185
|
-
|
|
186
|
-
For each branch target (e.g. `stable`, `beta`):
|
|
187
|
-
|
|
188
|
-
1. Build your mod content locally.
|
|
189
|
-
2. Stage it into a temp dir, generate a `workshopitem` VDF (without `publishedfileid`), and run `steamcmd +login $STEAM_USERNAME +workshop_build_item path/to/workshop.vdf +quit`.
|
|
190
|
-
3. Note the `publishedfileid` printed by SteamCMD.
|
|
191
|
-
4. Add the ID to your release config under `mods[*].workshopIds.<target>`.
|
|
192
|
-
|
|
193
|
-
For RimWorld specifically, the easier path is to publish the mod once via the in-game workshop publisher, then copy the workshop ID into your config.
|
|
194
|
-
|
|
195
|
-
After that, every push to `main` / `beta` will trigger an update via this plugin.
|
|
196
|
-
|
|
197
|
-
## Example `.steamignore`
|
|
198
|
-
|
|
199
|
-
Place at the root of each mod's `path` to exclude files from the upload. Patterns use rsync exclude syntax, plus gitignore-style `!` negation to re-include something a broader pattern would exclude. Later patterns win over earlier ones (gitignore semantics), so put negations after the patterns they override.
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
# Keep .run/ even though dotfiles below are excluded
|
|
203
|
-
.*
|
|
204
|
-
!.run/
|
|
205
|
-
|
|
206
|
-
# Source control
|
|
207
|
-
.git
|
|
208
|
-
.gitignore
|
|
209
|
-
.gitattributes
|
|
210
|
-
|
|
211
|
-
# Docs and templates (the workshop description comes from README.template.md, not these)
|
|
212
|
-
README.md
|
|
213
|
-
README.template.md
|
|
214
|
-
*.md
|
|
215
|
-
|
|
216
|
-
# Build inputs (the staged dir should contain only runtime artifacts)
|
|
217
|
-
Source/
|
|
218
|
-
src/
|
|
219
|
-
*.csproj
|
|
220
|
-
*.sln
|
|
221
|
-
|
|
222
|
-
# CI and local dev clutter
|
|
223
|
-
.github/
|
|
224
|
-
.vscode/
|
|
225
|
-
.idea/
|
|
226
|
-
node_modules/
|
|
227
|
-
|
|
228
|
-
# OS junk
|
|
229
|
-
.DS_Store
|
|
230
|
-
Thumbs.db
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
## How the README gets compiled into a workshop description
|
|
234
|
-
|
|
235
|
-
Each mod is expected to have a `README.template.md`. The plugin:
|
|
236
|
-
|
|
237
|
-
1. Concatenates `descriptionHeader + zeroWidthSeparator + template + zeroWidthSeparator + descriptionFooter`.
|
|
238
|
-
2. Runs an asset fallback substitution: any `../.github/assets/fallback/<name>` reference is replaced with `../.github/assets/<modspecificdir>/<name>` if a matching file exists. `<modspecificdir>` is resolved from `assetDirNameTransform`.
|
|
239
|
-
3. Rewrites `../.github/assets/` paths to absolute URLs using `assetBaseUrlTemplate` (so Steam's BBCode renderer can fetch them).
|
|
240
|
-
4. Pipes the result through `@steamdown/core` to convert Markdown to Steam BBCode.
|
|
241
|
-
5. Writes the BBCode into the workshop item description field.
|
|
39
|
+
`branchTargets` maps each release branch to a Workshop target. Each mod's `workshopIds` maps those targets to existing Steam Workshop item IDs.
|
|
242
40
|
|
|
243
|
-
|
|
41
|
+
Set these variables in your release environment:
|
|
244
42
|
|
|
245
|
-
|
|
43
|
+
- `STEAM_USERNAME`
|
|
44
|
+
- `STEAM_CONFIG_VDF` or `STEAM_CONFIG_VDF_B64`
|
|
45
|
+
- `STEAMCMD_PATH` if SteamCMD isn't at `~/steamcmd/steamcmd.sh`
|
|
246
46
|
|
|
247
|
-
|
|
47
|
+
SteamCMD must have a saved login in its `config/config.vdf`. See the [SteamCMD Workshop upload guide](https://partner.steamgames.com/doc/features/workshop/implementation#SteamCmdIntegration) for the item setup and VDF format.
|
|
248
48
|
|
|
249
|
-
|
|
49
|
+
Put `README.template.md` in each mod directory. Add `.steamignore` there if you need to exclude files from the upload.
|
|
250
50
|
|
|
251
|
-
|
|
252
|
-
- **`README.md` is no longer written into each mod dir by default.** If you relied on this (e.g. committing it via `@semantic-release/git`), set `outputReadme: true`.
|
|
253
|
-
- **Misconfigurations now throw instead of silently skipping.** Specifically: a mod with empty `workshopIds`, a mod missing the target key when no other mod has it either, or a misspelled target key (caught via did-you-mean).
|
|
254
|
-
- **Default upload timeout raised from 2 to 10 minutes.** Set `uploadTimeoutMs` to override.
|
|
51
|
+
The plugin updates Workshop items. Create each item before your first release, then add its ID to `workshopIds`.
|
|
255
52
|
|
|
256
|
-
|
|
53
|
+
## Documentation
|
|
257
54
|
|
|
258
|
-
-
|
|
259
|
-
-
|
|
260
|
-
-
|
|
261
|
-
-
|
|
262
|
-
- `verbose` option to surface SteamCMD output at info level.
|
|
263
|
-
- TypeScript declaration file shipped (`index.d.ts`) and JSON schema (`schema/plugin-config.json`).
|
|
55
|
+
- [Plugin option reference](./schema/plugin-config.json)
|
|
56
|
+
- [semantic-release configuration](https://semantic-release.gitbook.io/semantic-release/usage/configuration)
|
|
57
|
+
- [GitHub Actions setup](https://semantic-release.gitbook.io/semantic-release/recipes/ci-configurations/github-actions)
|
|
58
|
+
- [Steam Workshop implementation guide](https://partner.steamgames.com/doc/features/workshop/implementation)
|
|
264
59
|
|
|
265
60
|
## License
|
|
266
61
|
|
|
267
|
-
MIT
|
|
62
|
+
[MIT](./LICENSE)
|
package/index.mjs
CHANGED
|
@@ -19,6 +19,40 @@ function resolveModMetadata(mod, target) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// the context overrides are the seam the tests inject through; real runs get the imports.
|
|
23
|
+
function resolveOverrides(context) {
|
|
24
|
+
return {
|
|
25
|
+
buildDescription: context.buildSteamDescription ?? buildSteamDescription,
|
|
26
|
+
stageContent: context.stageModContent ?? stageModContent,
|
|
27
|
+
uploadItem: context.uploadWorkshopItem ?? uploadWorkshopItem,
|
|
28
|
+
compile: context.compileReadme ?? compileReadme,
|
|
29
|
+
writeCompiled: context.writeCompiledReadme ?? writeCompiledReadme,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function compileModReadme(pluginConfig, modPath, overrides) {
|
|
34
|
+
const compileArgs = {
|
|
35
|
+
modPath,
|
|
36
|
+
header: pluginConfig.descriptionHeader ?? '',
|
|
37
|
+
footer: pluginConfig.descriptionFooter ?? '',
|
|
38
|
+
assetDirNameTransform: pluginConfig.assetDirNameTransform,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return pluginConfig.outputReadme ? overrides.writeCompiled(compileArgs) : overrides.compile(compileArgs);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function describeDryRun({ mod, target, publishedFileId, version, description, metadata }) {
|
|
45
|
+
const details = [
|
|
46
|
+
`changenote: ${version}`,
|
|
47
|
+
`description: ${description.length} chars`,
|
|
48
|
+
metadata.title ? `title: "${metadata.title}"` : null,
|
|
49
|
+
metadata.visibility !== undefined ? `visibility: ${metadata.visibility}` : null,
|
|
50
|
+
metadata.tags?.length ? `tags: [${metadata.tags.join(', ')}]` : null,
|
|
51
|
+
].filter(Boolean);
|
|
52
|
+
|
|
53
|
+
return `[dry-run] would publish ${mod.name} to ${target} workshop item ${publishedFileId} (${details.join(', ')})`;
|
|
54
|
+
}
|
|
55
|
+
|
|
22
56
|
export async function verifyConditions(pluginConfig, context) {
|
|
23
57
|
await verifySteamPublishConfig({
|
|
24
58
|
env: context.env,
|
|
@@ -44,33 +78,16 @@ export async function publish(pluginConfig, context) {
|
|
|
44
78
|
|
|
45
79
|
const cwd = context.cwd ?? process.cwd();
|
|
46
80
|
const dryRun = isDryRun(context);
|
|
47
|
-
const
|
|
48
|
-
const stageContent = context.stageModContent ?? stageModContent;
|
|
49
|
-
const uploadItem = context.uploadWorkshopItem ?? uploadWorkshopItem;
|
|
50
|
-
const compile = context.compileReadme ?? compileReadme;
|
|
51
|
-
const writeCompiled = context.writeCompiledReadme ?? writeCompiledReadme;
|
|
81
|
+
const overrides = resolveOverrides(context);
|
|
52
82
|
const assetBaseUrl = pluginConfig.assetBaseUrlTemplate
|
|
53
83
|
? pluginConfig.assetBaseUrlTemplate.replace('{branch}', context.branch.name)
|
|
54
84
|
: '';
|
|
55
85
|
|
|
56
86
|
for (const mod of state.mods) {
|
|
57
87
|
const modPath = resolve(cwd, mod.path);
|
|
88
|
+
const markdown = await compileModReadme(pluginConfig, modPath, overrides);
|
|
58
89
|
|
|
59
|
-
const
|
|
60
|
-
modPath,
|
|
61
|
-
header: pluginConfig.descriptionHeader ?? '',
|
|
62
|
-
footer: pluginConfig.descriptionFooter ?? '',
|
|
63
|
-
assetDirNameTransform: pluginConfig.assetDirNameTransform,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
let markdown;
|
|
67
|
-
if (pluginConfig.outputReadme) {
|
|
68
|
-
markdown = await writeCompiled(compileArgs);
|
|
69
|
-
} else {
|
|
70
|
-
markdown = await compile(compileArgs);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const description = await buildDescription({
|
|
90
|
+
const description = await overrides.buildDescription({
|
|
74
91
|
modPath,
|
|
75
92
|
markdown,
|
|
76
93
|
assetBaseUrl,
|
|
@@ -82,19 +99,21 @@ export async function publish(pluginConfig, context) {
|
|
|
82
99
|
|
|
83
100
|
if (dryRun) {
|
|
84
101
|
context.logger.log(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
102
|
+
describeDryRun({
|
|
103
|
+
mod,
|
|
104
|
+
target: state.target,
|
|
105
|
+
publishedFileId,
|
|
106
|
+
version: context.nextRelease.version,
|
|
107
|
+
description,
|
|
108
|
+
metadata,
|
|
109
|
+
}),
|
|
91
110
|
);
|
|
92
111
|
continue;
|
|
93
112
|
}
|
|
94
113
|
|
|
95
|
-
const stagePath = await stageContent({ modPath });
|
|
114
|
+
const stagePath = await overrides.stageContent({ modPath });
|
|
96
115
|
|
|
97
|
-
await uploadItem({
|
|
116
|
+
await overrides.uploadItem({
|
|
98
117
|
steamCmdPath: context.env.STEAMCMD_PATH ?? '~/steamcmd/steamcmd.sh',
|
|
99
118
|
steamUsername: context.env.STEAM_USERNAME,
|
|
100
119
|
steamConfigPath: state.steamConfigPath,
|
package/lib/description.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { rewriteAssetLinksForSteam } from './readme.mjs';
|
|
|
7
7
|
const FALLBACK_DESCRIPTION = 'No description available.';
|
|
8
8
|
|
|
9
9
|
function stripFenceInfoStrings(markdown) {
|
|
10
|
-
return markdown.replace(
|
|
10
|
+
return markdown.replace(/(?<=^[^\S\r\n]*`{3,})[^\r\n`]*$/gm, '');
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function renderSteamBBCode(markdown) {
|
package/lib/readme.mjs
CHANGED
|
@@ -25,11 +25,11 @@ function matchGlob(pattern, candidates) {
|
|
|
25
25
|
const re = new RegExp(
|
|
26
26
|
'^' +
|
|
27
27
|
pattern
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
.
|
|
31
|
-
.
|
|
32
|
-
.
|
|
28
|
+
.replaceAll(/[.+^${}()|\\]/g, String.raw`\$&`)
|
|
29
|
+
.replaceAll('**', '[__DOUBLESTAR__]')
|
|
30
|
+
.replaceAll('*', '[^/]*')
|
|
31
|
+
.replaceAll('?', '[^/]')
|
|
32
|
+
.replaceAll('[__DOUBLESTAR__]', '.*') +
|
|
33
33
|
'$',
|
|
34
34
|
);
|
|
35
35
|
return candidates.filter(c => re.test(c));
|
|
@@ -40,25 +40,17 @@ async function resolveAssetDirs(transform, modPath, repoRoot) {
|
|
|
40
40
|
return transform(modPath);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (Array.isArray(transform)) {
|
|
44
|
-
|
|
45
|
-
const subdirs = transform.some(isGlobPattern) ? await listAssetSubdirs(assetsRoot) : [];
|
|
46
|
-
const resolved = [];
|
|
47
|
-
|
|
48
|
-
for (const entry of transform) {
|
|
49
|
-
if (isGlobPattern(entry)) {
|
|
50
|
-
for (const match of matchGlob(entry, subdirs)) {
|
|
51
|
-
if (!resolved.includes(match)) resolved.push(match);
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
if (!resolved.includes(entry)) resolved.push(entry);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return resolved;
|
|
43
|
+
if (!Array.isArray(transform)) {
|
|
44
|
+
return defaultAssetDirNameTransform(modPath);
|
|
59
45
|
}
|
|
60
46
|
|
|
61
|
-
|
|
47
|
+
const assetsRoot = join(repoRoot, '.github', 'assets');
|
|
48
|
+
const subdirs = transform.some(isGlobPattern) ? await listAssetSubdirs(assetsRoot) : [];
|
|
49
|
+
const matched = transform.flatMap(entry =>
|
|
50
|
+
isGlobPattern(entry) ? matchGlob(entry, subdirs) : [entry],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return [...new Set(matched)];
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
async function replaceFallbackAssets(markdown, modPath, assetDirNameTransform) {
|
package/lib/vdf.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
function escapeVdfValue(value) {
|
|
2
2
|
let open = true;
|
|
3
|
-
return String(value ?? '').
|
|
3
|
+
return String(value ?? '').replaceAll('"', () => {
|
|
4
4
|
const quote = open ? '“' : '”';
|
|
5
5
|
open = !open;
|
|
6
6
|
return quote;
|
|
@@ -14,8 +14,7 @@ function appendOptional(lines, key, value) {
|
|
|
14
14
|
|
|
15
15
|
function appendTags(lines, tags) {
|
|
16
16
|
if (!tags || !Array.isArray(tags) || tags.length === 0) return;
|
|
17
|
-
lines.push(' "tags"');
|
|
18
|
-
lines.push(' {');
|
|
17
|
+
lines.push(' "tags"', ' {');
|
|
19
18
|
for (let i = 0; i < tags.length; i++) {
|
|
20
19
|
lines.push(` "${i}" "${escapeVdfValue(tags[i])}"`);
|
|
21
20
|
}
|
|
@@ -56,8 +55,7 @@ export function createWorkshopVdf({
|
|
|
56
55
|
}
|
|
57
56
|
appendTags(lines, tags);
|
|
58
57
|
|
|
59
|
-
lines.push('}');
|
|
60
|
-
lines.push('');
|
|
58
|
+
lines.push('}', '');
|
|
61
59
|
|
|
62
60
|
return lines.join('\n');
|
|
63
61
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "semantic-release-steam",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"description": "semantic-release plugin that publishes a Steam Workshop item from a built mod directory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"license": "MIT",
|
|
45
45
|
"repository": {
|
|
46
46
|
"type": "git",
|
|
47
|
-
"url": "git+https://github.com/
|
|
47
|
+
"url": "git+https://github.com/RimWorks/semantic-release-steam.git"
|
|
48
48
|
},
|
|
49
49
|
"bugs": {
|
|
50
|
-
"url": "https://github.com/
|
|
50
|
+
"url": "https://github.com/RimWorks/semantic-release-steam/issues"
|
|
51
51
|
},
|
|
52
|
-
"homepage": "https://github.com/
|
|
52
|
+
"homepage": "https://github.com/RimWorks/semantic-release-steam#readme",
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=20.0.0"
|
|
55
55
|
},
|
|
@@ -65,12 +65,11 @@
|
|
|
65
65
|
"@steamdown/core": "^1.0.0-beta.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@eslint/js": "^
|
|
69
|
-
"@types/node": "^
|
|
70
|
-
"eslint": "^
|
|
71
|
-
"globals": "^
|
|
68
|
+
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@types/node": "^26.6.1",
|
|
70
|
+
"eslint": "^10.10.0",
|
|
71
|
+
"globals": "^17.12.0",
|
|
72
72
|
"semantic-release": "^25.0.0",
|
|
73
|
-
"
|
|
74
|
-
"typescript": "^5.5.0"
|
|
73
|
+
"typescript": "^7.0.2"
|
|
75
74
|
}
|
|
76
75
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://github.com/
|
|
3
|
+
"$id": "https://github.com/RimWorks/semantic-release-steam/schema/plugin-config.json",
|
|
4
4
|
"title": "semantic-release-steam plugin config",
|
|
5
5
|
"description": "Configuration for the semantic-release-steam plugin.",
|
|
6
6
|
"type": "object",
|