Add reusable source-release workflow#1
Conversation
Adds .github/workflows/source-release.yml — a reusable workflow that
packages a customer-facing source zip from a caller library on release
publish, and optionally uploads it to S3.
Driven by an allow-list of csproj paths in the caller repository; uses
the new scripts/source-release/stage.sh to enumerate the exact source,
.props/.targets, and strong-name key each project needs via
`dotnet msbuild -getItem -getProperty -pp`.
The workflow expects build-common to be checked out as a submodule at
the caller's repository root, pinned to a commit that includes
scripts/source-release/stage.sh.
Pipeline:
package - stage, scan for secrets (license keys, nuget.config,
credential markers), zip, upload artifact.
verify-build - extract on a fresh runner and `dotnet build` the zip
with a real Avalonia license key.
upload - aws-actions/configure-aws-credentials@v4 + `aws s3 cp`
to s3://<bucket>/<prefix>/<zip>. Gated by upload_to_s3.
This was extracted from the per-repo implementation prototyped in
AvaloniaUI/Avalonia.Controls.TreeDataGrid-Accelerate#125.
|
Things that need to be done before merging this:
|
Lets a caller's `workflow_dispatch` trigger exercise the full pipeline
without publishing a release: the caller passes an explicit `version`
input that takes precedence over `github.event.release.tag_name`, and
sets `upload_to_s3: false` so the test zip stays in workflow artifacts
only.
The version-resolution step now errors clearly when neither source is
available, instead of silently building "${PROJECT_NAME}-.zip" off an
empty tag.
There was a problem hiding this comment.
Pull request overview
Adds a reusable GitHub Actions workflow to generate and optionally upload a “customer-facing” source-release zip for consumer libraries, plus the staging script and README usage docs to support it.
Changes:
- Added
scripts/source-release/stage.shto enumerate required repo files via MSBuild and stage a minimal buildable source tree. - Added reusable workflow
.github/workflows/source-release.ymlto stage → scan → zip → verify build → (optionally) upload to S3. - Updated
README.mdwith consumer setup instructions and a caller workflow snippet.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
scripts/source-release/stage.sh |
New staging script that derives an allow-listed, MSBuild-driven source file set and stages it for packaging. |
README.md |
Documents how consumer repos should call the reusable workflow and what they must provide. |
.github/workflows/source-release.yml |
New reusable workflow implementing source-release packaging, verification build, and optional S3 upload. |
Comments suppressed due to low confidence (5)
scripts/source-release/stage.sh:133
- The repo-relative filtering/deduping loop assumes each collected
pathis absolute (or that the current working directory is the repo root). Because MSBuild item identities can be relative,readlink -m "$path"may resolve relative paths against the caller’s cwd, causing valid repo files to be dropped or incorrect paths to be staged. Considercd "$repo_root"once up-front and/or normalize non-absolute paths by prefixingrepo_rootbeforereadlink/realpath.
# Filter to repo-relative paths and dedupe.
while IFS= read -r path; do
[[ -z "$path" ]] && continue
abs=$(readlink -m "$path")
if [[ "$abs" == "$repo_root"/* ]]; then
echo "${abs#"$repo_root"/}"
fi
done < "$files_raw" | sort -u > "$files_relative"
scripts/source-release/stage.sh:38
- This script relies on GNU-specific utilities/options (
readlink -m/-f, and potentiallycp -a). If consumers may run staging on macOS runners (or locally), it will fail. Either document that staging is Linux-only, or switch to more portable equivalents (e.g.,realpathwith fallbacks) and avoid GNU-only flags.
repo_root=$(cd "$1" && pwd)
allow_list=$(readlink -f "$2")
staging_dir=$(readlink -m "$3")
.github/workflows/source-release.yml:242
- The reusable workflow requires a
.slnxat the extracted zip root, andstage.shwill also hard-fail if no.slnxexists in the caller repo root. This requirement isn’t mentioned in the caller-facing docs/inputs; either support.slnas a fallback in both staging and verification, or document the.slnxrequirement explicitly for consumers.
with:
name: source-zip
path: ${{ runner.temp }}/zip
- name: Extract zip
shell: bash
run: |
.github/workflows/source-release.yml:103
raw_tag='${{ github.event.release.tag_name }}'can break if a tag name contains a single quote, and it embeds the value directly into the script text. Prefer passing the tag as an env var (or reading from a predefined Actions env) and assigning it with normal quoting (raw_tag="$RAW_TAG") to avoid quoting/injection edge cases.
dotnet-version: ${{ inputs.dotnet_sdk }}
- name: Resolve version
id: version
shell: bash
.github/workflows/source-release.yml:57
checkout_tokenis marked required, but many callers can safely use the defaultgithub.token(as done inlibrary-cicd.yml). Consider making this secret optional and falling back togithub.tokenin the checkout step, while still allowing PATs for private submodules.
required: false
type: string
default: ubuntu-latest
dotnet_sdk:
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
stage.sh: - Resolve <allow-list> arg relative to <repo-root> when not absolute, so the path semantics match what the usage docs claim. Fail with a clear `::error::` message if the file is missing, instead of leaving that to `readlink -f`'s default error. - Resolve item paths from $repo_root in the repo-relative filtering loop so a relative .Identity (when MSBuild omits .FullPath) is interpreted the same way the project sees it, regardless of this script's CWD. source-release.yml: - Drop the `runs_on` input. The reusable workflow and stage.sh depend on Linux tooling (GNU readlink, jq, zip/unzip, AWS CLI); exposing `runs_on` as configurable suggested cross-platform support that doesn't exist. Hard-code `ubuntu-latest`. README.md: - Document the `.slnx` requirement and the Linux-only assumption in the caller prerequisites.
|
Addressed Copilot's review in 7a4c32d: Fixed:
Already addressed:
Declined (matches existing convention):
|
A branch ref (`@main`) makes every consumer immediately inherit any commit to this repo. SHA pinning makes upgrades intentional and gives Dependabot a target to track. Note that the caller's PAT / submodule pin already enforces a similar discipline for stage.sh — this aligns the workflow ref with the same model. Recommends SHA pinning in the consumer example. Doesn't change the workflow itself; internal `uses:` lines still pin to `@v4` major tags, matching the convention in library-cicd.yml.
When a repo has more than one .slnx at its root (e.g. a `*.ci.slnx` or `*.Mcp.slnx`), the stager's `find ... -print -quit` returns whichever one filesystem ordering hands back first — which on Avalonia.Controls.Charts is `*.ci.slnx`, not the canonical `Avalonia.Controls.Charts.slnx` customers expect. Add an explicit `solution_file` input to the reusable workflow and plumb it through to `stage.sh` as an optional 4th positional arg. When unset, behavior is unchanged (single-.slnx repos keep working without configuration). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lets workflow_dispatch check out an arbitrary commitish and toggle the S3 upload, so older versions can be re-packaged without a release event. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the bucket-name-only s3_bucket secret with s3_bucket_endpoint, a virtual-hosted-style URL the workflow parses into bucket + service endpoint and passes to `aws s3 cp --endpoint-url`. Lets uploads target Scaleway (or any S3-compatible service) as well as plain AWS. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
It calls sts.<region>.amazonaws.com to validate credentials, which fails against non-AWS endpoints like Scaleway (`getaddrinfo ENOTFOUND sts.fr-par.amazonaws.com`). Set AWS_* env vars on the upload step directly — the AWS CLI then talks to S3 via --endpoint-url without ever contacting STS. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Tested with all Pro components and S3 upload implemented. Ready for review. |
- upload_to_s3 defaults to false so callers must opt in explicitly;
release-triggered callers pass `${{ github.event_name == 'release' }}`.
- s3_bucket_endpoint regex splits on `.s3.` rather than the first dot,
so virtual-hosted URLs with dotted bucket names
(e.g. https://my.bucket.s3.us-east-1.amazonaws.com) parse correctly.
- stage.sh registers each `pp_file` with the script-level EXIT trap so
the temp file is cleaned up even when the dotnet preprocess call
fails under `set -e`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Adds a reusable workflow (
workflow_call) that packages a customer-facing source zip from a consumer library when a release is published, and the staging script it relies on..github/workflows/source-release.yml— the reusable workflow. Three jobs:package(stage → scan → zip → upload artifact),verify-build(extract on fresh runner,dotnet buildwith a real license key), andupload(configure AWS,aws s3 cp; gated by theupload_to_s3input).scripts/source-release/stage.sh— bash + MSBuild + jq script that enumerates the exact files a customer build needs, viadotnet msbuild -getItem -getPropertyfor items and properties (includingAssemblyOriginatorKeyFile) and-pp(preprocess) for transitive.props/.targetsimports. Reliable across modern MSBuild versions where$(MSBuildAllProjects)is unreliable.README.md— adds a "Source-release workflow" section with the caller snippet and consumer requirements.The workflow assumes
build-commonis checked out as a submodule at the caller's repository root and pinned to a SHA that containsscripts/source-release/stage.sh— aVerify staging script is presentstep fails with a clear message if not.Consumer API
Naming conventions (
snake_caseinputs/secrets,@mainref) match the existinglibrary-cicd.yml.Testing without publishing a release
The reusable workflow accepts an optional
versioninput. When non-empty it overridesgithub.event.release.tag_name, letting callers wire up aworkflow_dispatchtrigger (as in the snippet above) that runs the full pipeline — stage, scan, zip, verify-build — against an arbitrary version. Pairing it withupload_to_s3: ${{ github.event_name == 'release' }}ensures the test zip stays in workflow artifacts and never touches S3. The version-resolution step errors clearly if neither source is set.Provenance
Extracted from the per-repo implementation prototyped and end-to-end validated in AvaloniaUI/Avalonia.Controls.TreeDataGrid-Accelerate#125. That PR will be converted to consume this reusable workflow once this lands.
Test plan
Avalonia.Controls.TreeDataGrid-Accelerate: bump thebuild-commonsubmodule pin, replace the localsource-release.yml+stage.shwith the caller wrapper (already done in the converted #125), and run an end-to-endworkflow_dispatchtest followed by a real release test.Avalonia.Controls.MediaPlayer,Avalonia.Controls.Keyboard,Avalonia.Controls.RichTextEditor,Avalonia.Controls.Charts.🤖 Generated with Claude Code