semantic-release-steam 2.1.1 → 2.1.3

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 CHANGED
@@ -1,69 +1,33 @@
1
1
  # semantic-release-steam
2
2
 
3
- A [semantic-release](https://github.com/semantic-release/semantic-release) plugin that publishes a built Steam Workshop item via [SteamCMD](https://developer.valvesoftware.com/wiki/SteamCMD).
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 -D semantic-release-steam
8
+ npm install --save-dev semantic-release-steam
27
9
  ```
28
10
 
29
- You'll also need:
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
- ## Configuration
13
+ ## Configure
38
14
 
39
- In `release.config.mjs`:
15
+ Add the plugin to `release.config.mjs`:
40
16
 
41
17
  ```js
42
18
  export default {
43
- branches: ['main', { name: 'beta', prerelease: true }],
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', beta: 'beta' },
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: 'MyMod',
59
- path: 'MyMod',
60
- workshopIds: { stable: '1234567890', beta: '1234567891' },
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
- ### Plugin options
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
- If a mod has no `README.template.md` (and `outputReadme` is off, leaving no `README.md` to fall back on), the description defaults to `"No description available."`.
41
+ Set these variables in your release environment:
244
42
 
245
- By default the compiled markdown stays in memory and never touches your repo. Set `outputReadme: true` if you want the compiled `README.md` written to `<modPath>/README.md` (e.g. so `@semantic-release/git` can commit it).
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
- ## Upgrading from 1.x
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
- Breaking changes in 2.0:
49
+ Put `README.template.md` in each mod directory. Add `.steamignore` there if you need to exclude files from the upload.
250
50
 
251
- - **`@steamdown/cli` is no longer required.** The plugin now uses `@steamdown/core` directly. Remove `npm install -g @steamdown/cli` from your CI.
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
- New in 2.0:
53
+ ## Documentation
257
54
 
258
- - `STEAM_CONFIG_VDF_B64` env var accepted alongside `STEAM_CONFIG_VDF`.
259
- - Optional workshop metadata: `title`, `previewfile`, `visibility`, `tags`. Per-mod defaults with per-target overrides via `mod.metadata[target]`.
260
- - `assetDirNameTransform` accepts an array (literal or glob) in addition to a function.
261
- - `semantic-release --dry-run` is honored.
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 - see [LICENSE](./LICENSE).
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 buildDescription = context.buildSteamDescription ?? buildSteamDescription;
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 compileArgs = {
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
- `[dry-run] would publish ${mod.name} to ${state.target} workshop item ${publishedFileId} ` +
86
- `(changenote: ${context.nextRelease.version}, description: ${description.length} chars` +
87
- (metadata.title ? `, title: "${metadata.title}"` : '') +
88
- (metadata.visibility !== undefined ? `, visibility: ${metadata.visibility}` : '') +
89
- (metadata.tags?.length ? `, tags: [${metadata.tags.join(', ')}]` : '') +
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,
@@ -6,12 +6,16 @@ import { rewriteAssetLinksForSteam } from './readme.mjs';
6
6
 
7
7
  const FALLBACK_DESCRIPTION = 'No description available.';
8
8
 
9
+ function stripFenceInfoStrings(markdown) {
10
+ return markdown.replace(/(?<=^[^\S\r\n]*`{3,})[^\r\n`]*$/gm, '');
11
+ }
12
+
9
13
  export function renderSteamBBCode(markdown) {
10
14
  if (!markdown || markdown.trim().length === 0) {
11
15
  return FALLBACK_DESCRIPTION;
12
16
  }
13
17
 
14
- const [tree, context] = parse(markdown);
18
+ const [tree, context] = parse(stripFenceInfoStrings(markdown));
15
19
  const rendered = render(tree, context);
16
20
 
17
21
  if (!rendered || rendered.trim().length === 0) {
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
- .replace(/[.+^${}()|\\]/g, '\\$&')
29
- .replace(/\*\*/g, '[__DOUBLESTAR__]')
30
- .replace(/\*/g, '[^/]*')
31
- .replace(/\?/g, '[^/]')
32
- .replace(/\[__DOUBLESTAR__\]/g, '.*') +
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
- const assetsRoot = join(repoRoot, '.github', 'assets');
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
- return defaultAssetDirNameTransform(modPath);
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 ?? '').replace(/"/g, () => {
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.1",
3
+ "version": "2.1.3",
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/cryptiklemur/semantic-release-steam.git"
47
+ "url": "git+https://github.com/RimWorks/semantic-release-steam.git"
48
48
  },
49
49
  "bugs": {
50
- "url": "https://github.com/cryptiklemur/semantic-release-steam/issues"
50
+ "url": "https://github.com/RimWorks/semantic-release-steam/issues"
51
51
  },
52
- "homepage": "https://github.com/cryptiklemur/semantic-release-steam#readme",
52
+ "homepage": "https://github.com/RimWorks/semantic-release-steam#readme",
53
53
  "engines": {
54
54
  "node": ">=20.0.0"
55
55
  },
@@ -70,7 +70,6 @@
70
70
  "eslint": "^9.0.0",
71
71
  "globals": "^15.0.0",
72
72
  "semantic-release": "^25.0.0",
73
- "@semantic-release/git": "^10.0.0",
74
73
  "typescript": "^5.5.0"
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/cryptiklemur/semantic-release-steam/schema/plugin-config.json",
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",