Skip to content

Commit bb018c8

Browse files
Merge pull request #3 from roc-lang/support-additional-release-assets
Support additional assets in publish-release
2 parents d2560ea + 48ccc90 commit bb018c8

5 files changed

Lines changed: 99 additions & 2 deletions

File tree

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,12 @@ the matching upload and download steps together.
564564
- `.release/release-bundles.json`
565565

566566
`publish-release` defaults to `.release/release-bundles.json` as its asset
567-
manifest and uploads only the listed `.tar.zst` files from `.release/bundles`.
567+
manifest and uploads the listed `.tar.zst` files from `.release/bundles`.
568+
Use its `additional_assets` input for newline-separated paths to other release
569+
files, such as documentation archives. All bundle and additional assets are
570+
passed to the same `gh release create` invocation so they are attached before
571+
the release is published. This is required when immutable releases are enabled;
572+
assets cannot be uploaded to an immutable release after publication.
568573

569574
For release candidates such as `1.2.3-rc1`, `resolve-previous-release` still
570575
uses GitHub's latest stable release as the default previous bundle. Set

actions/publish-release/action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ inputs:
1313
release_list_file:
1414
description: File listing release bundle assets produced by prepare-bundles.
1515
default: .release/release-bundles.json
16+
additional_assets:
17+
description: Newline-separated paths to additional files to attach before publishing the release.
18+
default: ""
1619
repo:
1720
description: GitHub repository in owner/name form. Empty uses GITHUB_REPOSITORY.
1821
default: ""
@@ -35,6 +38,7 @@ runs:
3538
RELEASE_NOTES_FILE: ${{ inputs.release_notes_file }}
3639
BUNDLE_DIR: ${{ inputs.bundle_dir }}
3740
RELEASE_LIST_FILE: ${{ inputs.release_list_file }}
41+
ADDITIONAL_ASSETS_INPUT: ${{ inputs.additional_assets }}
3842
REPO_INPUT: ${{ inputs.repo }}
3943
TARGET_INPUT: ${{ inputs.target }}
4044
CHECK_AVAILABILITY: ${{ inputs.check_availability }}
@@ -51,4 +55,5 @@ runs:
5155
--notes-file "$RELEASE_NOTES_FILE" \
5256
--bundle-dir "$BUNDLE_DIR" \
5357
--release-list-file "$RELEASE_LIST_FILE" \
58+
--additional-assets "$ADDITIONAL_ASSETS_INPUT" \
5459
--check-availability "$CHECK_AVAILABILITY"

scripts/release/release.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def build_parser() -> argparse.ArgumentParser:
125125
publish.add_argument("--notes-file", required=True)
126126
publish.add_argument("--bundle-dir", required=True)
127127
publish.add_argument("--release-list-file", required=True)
128+
publish.add_argument("--additional-assets", default="")
128129
publish.add_argument("--check-availability", choices=["true", "false"], default="true")
129130
publish.set_defaults(func=cmd_publish_release)
130131

@@ -494,6 +495,8 @@ def cmd_publish_release(args: argparse.Namespace) -> int:
494495
target = require_target(env_fallback(args.target, "GITHUB_SHA"))
495496
notes_file = require_nonempty_file(args.notes_file, "release notes file")
496497
assets = release_assets(args.bundle_dir, args.release_list_file)
498+
assets.extend(additional_release_assets(args.additional_assets))
499+
require_unique_release_asset_names(assets)
497500

498501
if args.check_availability == "true":
499502
check_availability(version, repo)
@@ -1147,6 +1150,28 @@ def release_assets(bundle_dir_text: str | Path, release_list_file_text: str | Pa
11471150
return assets
11481151

11491152

1153+
def additional_release_assets(value: str) -> list[Path]:
1154+
assets: list[Path] = []
1155+
for line in value.splitlines():
1156+
path_text = line.strip()
1157+
if not path_text:
1158+
continue
1159+
if "#" in path_text:
1160+
raise ReleaseError(
1161+
f"additional release asset path must not contain '#': {path_text}"
1162+
)
1163+
assets.append(require_nonempty_file(path_text, "additional release asset"))
1164+
return assets
1165+
1166+
1167+
def require_unique_release_asset_names(assets: list[Path]) -> None:
1168+
seen: set[str] = set()
1169+
for asset in assets:
1170+
if asset.name in seen:
1171+
raise ReleaseError(f"duplicate release asset filename: {asset.name}")
1172+
seen.add(asset.name)
1173+
1174+
11501175
def run(command: list[str]) -> subprocess.CompletedProcess[str]:
11511176
return subprocess.run(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
11521177

tests/fixtures/composite-actions.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ jobs:
7373
- run: |
7474
mkdir -p .release/bundles
7575
printf 'bundle' > .release/bundles/pkg.tar.zst
76+
printf 'docs' > docs.tar.gz
7677
printf '[{"name":"default","artifact_file":"pkg.tar.zst","source_path":"dist/pkg.tar.zst"}]\n' > .release/release-bundles.json
7778
7879
- uses: ./actions/make-release-notes
@@ -84,6 +85,7 @@ jobs:
8485
- uses: ./actions/publish-release
8586
with:
8687
release_version: ${{ needs.build.outputs.release_version }}
88+
additional_assets: docs.tar.gz
8789
check_availability: "false"
8890

8991
docs:

tests/test_release.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,15 @@ def test_create_followup_pr_rejects_invalid_branch_prefix(self):
777777

778778
# --- publish-release ---
779779

780-
def publish_args(self, version="1.2.3", notes=None, bundle_dir=None, release_list=None, check_availability="true"):
780+
def publish_args(
781+
self,
782+
version="1.2.3",
783+
notes=None,
784+
bundle_dir=None,
785+
release_list=None,
786+
additional_assets="",
787+
check_availability="true",
788+
):
781789
return parse_args(
782790
"publish-release",
783791
"--version", version,
@@ -786,6 +794,7 @@ def publish_args(self, version="1.2.3", notes=None, bundle_dir=None, release_lis
786794
"--notes-file", str(notes),
787795
"--bundle-dir", str(bundle_dir),
788796
"--release-list-file", str(release_list),
797+
"--additional-assets", additional_assets,
789798
"--check-availability", check_availability,
790799
)
791800

@@ -835,6 +844,57 @@ def test_publish_release_rechecks_availability_and_creates_release(self):
835844
self.assertNotIn("debug.txt", commands)
836845
self.assertIn("--notes-file", commands)
837846

847+
def test_publish_release_includes_additional_assets(self):
848+
log = self.tmp / "commands.log"
849+
notes = self.write("release-notes.md", "Generated notes\n")
850+
bundle_dir = self.tmp / "bundles"
851+
bundle_dir.mkdir()
852+
self.write("bundles/pkg.tar.zst", "bundle")
853+
docs = self.write("docs.tar.gz", "docs")
854+
release_list = self.write(
855+
"release-bundles.json",
856+
json.dumps([{"name": "default", "artifact_file": "pkg.tar.zst"}]),
857+
)
858+
with self.fake_commands(
859+
env={"LOG_PATH": str(log)},
860+
git='echo "git:$*" >> "$LOG_PATH"\n',
861+
gh='echo "gh:$*" >> "$LOG_PATH"\n',
862+
):
863+
release.cmd_publish_release(
864+
self.publish_args(
865+
notes=notes,
866+
bundle_dir=bundle_dir,
867+
release_list=release_list,
868+
additional_assets=f"\n{docs}\n",
869+
check_availability="false",
870+
)
871+
)
872+
commands = log.read_text(encoding="utf-8")
873+
self.assertIn(f"gh:release create 1.2.3 {bundle_dir / 'pkg.tar.zst'} {docs}", commands)
874+
875+
def test_publish_release_rejects_duplicate_additional_asset_filename(self):
876+
notes = self.write("release-notes.md", "Generated notes\n")
877+
bundle_dir = self.tmp / "bundles"
878+
bundle_dir.mkdir()
879+
self.write("bundles/pkg.tar.zst", "bundle")
880+
additional_dir = self.tmp / "additional"
881+
additional_dir.mkdir()
882+
duplicate = self.write("additional/pkg.tar.zst", "duplicate")
883+
release_list = self.write(
884+
"release-bundles.json",
885+
json.dumps([{"name": "default", "artifact_file": "pkg.tar.zst"}]),
886+
)
887+
with self.assertRaisesRegex(release.ReleaseError, "duplicate release asset filename"):
888+
release.cmd_publish_release(
889+
self.publish_args(
890+
notes=notes,
891+
bundle_dir=bundle_dir,
892+
release_list=release_list,
893+
additional_assets=str(duplicate),
894+
check_availability="false",
895+
)
896+
)
897+
838898
def test_publish_release_marks_prerelease(self):
839899
log = self.tmp / "commands.log"
840900
notes = self.write("release-notes.md", "Generated notes\n")

0 commit comments

Comments
 (0)