Skip to content

Add reusable source-release workflow#1

Open
grokys wants to merge 9 commits into
mainfrom
add-source-release-workflow
Open

Add reusable source-release workflow#1
grokys wants to merge 9 commits into
mainfrom
add-source-release-workflow

Conversation

@grokys
Copy link
Copy Markdown
Member

@grokys grokys commented May 18, 2026

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 build with a real license key), and upload (configure AWS, aws s3 cp; gated by the upload_to_s3 input).
  • scripts/source-release/stage.sh — bash + MSBuild + jq script that enumerates the exact files a customer build needs, via dotnet msbuild -getItem -getProperty for items and properties (including AssemblyOriginatorKeyFile) and -pp (preprocess) for transitive .props/.targets imports. 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-common is checked out as a submodule at the caller's repository root and pinned to a SHA that contains scripts/source-release/stage.sh — a Verify staging script is present step fails with a clear message if not.

Consumer API

on:
    release:
        types: [published]
    workflow_dispatch:
        inputs:
            version:
                description: 'Version for the test zip (no leading v).'
                required: true
                default: 0.0.0-test1

jobs:
    source-release:
        uses: AvaloniaUI/build-common/.github/workflows/source-release.yml@main
        with:
            project_name: Avalonia.Controls.Example
            version: ${{ inputs.version }}                       # empty on release events
            upload_to_s3: ${{ github.event_name == 'release' }}  # only upload real releases
        secrets:
            checkout_token: ${{ secrets.SUBMODULE_TOKEN }}
            license_key: ${{ secrets.ACCELERATE_LICENSE_KEY }}
            aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
            aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            aws_region: ${{ secrets.AWS_REGION }}
            s3_bucket: ${{ secrets.SOURCE_S3_BUCKET }}

Naming conventions (snake_case inputs/secrets, @main ref) match the existing library-cicd.yml.

Testing without publishing a release

The reusable workflow accepts an optional version input. When non-empty it overrides github.event.release.tag_name, letting callers wire up a workflow_dispatch trigger (as in the snippet above) that runs the full pipeline — stage, scan, zip, verify-build — against an arbitrary version. Pairing it with upload_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

  • Merge this PR.
  • In Avalonia.Controls.TreeDataGrid-Accelerate: bump the build-common submodule pin, replace the local source-release.yml + stage.sh with the caller wrapper (already done in the converted #125), and run an end-to-end workflow_dispatch test followed by a real release test.
  • Roll out to Avalonia.Controls.MediaPlayer, Avalonia.Controls.Keyboard, Avalonia.Controls.RichTextEditor, Avalonia.Controls.Charts.

🤖 Generated with Claude Code

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.
@grokys grokys marked this pull request as draft May 18, 2026 12:29
@grokys
Copy link
Copy Markdown
Member Author

grokys commented May 18, 2026

Things that need to be done before merging this:

  1. Set up an endpoint on Scaleway to accept the packages
  2. Test on all relevant products

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.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.sh to enumerate required repo files via MSBuild and stage a minimal buildable source tree.
  • Added reusable workflow .github/workflows/source-release.yml to stage → scan → zip → verify build → (optionally) upload to S3.
  • Updated README.md with 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 path is 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. Consider cd "$repo_root" once up-front and/or normalize non-absolute paths by prefixing repo_root before readlink/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 potentially cp -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., realpath with 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 .slnx at the extracted zip root, and stage.sh will also hard-fail if no .slnx exists in the caller repo root. This requirement isn’t mentioned in the caller-facing docs/inputs; either support .sln as a fallback in both staging and verification, or document the .slnx requirement 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_token is marked required, but many callers can safely use the default github.token (as done in library-cicd.yml). Consider making this secret optional and falling back to github.token in 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.

Comment thread scripts/source-release/stage.sh Outdated
Comment thread .github/workflows/source-release.yml
Comment thread README.md
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.
@grokys
Copy link
Copy Markdown
Member Author

grokys commented May 18, 2026

Addressed Copilot's review in 7a4c32d:

Fixed:

  • allow_list path resolution — now resolves relative to <repo-root> when not absolute, matching the usage docs, with an explicit existence check and clear ::error:: message.
  • runs_on input dropped. The workflow and stage.sh depend on Linux tooling (GNU readlink, jq, zip/unzip, AWS CLI), so the input was advertising flexibility that didn't exist. Hard-coded to ubuntu-latest.
  • README now documents the .slnx-at-root requirement and the Linux-only assumption.
  • Repo-relative filtering loop: readlink -m now runs from $repo_root so any relative .Identity from MSBuild resolves the way the project sees it.

Already addressed:

  • The raw_tag quoting concern was fixed in ef079e4 — version resolution now reads RAW_TAG from an env var rather than interpolating the tag literal into the shell script.

Declined (matches existing convention):

  • checkout_token stays required, matching library-cicd.yml. Worth revisiting org-wide if we want a github.token fallback for libraries without private submodules, but out of scope here.

grokys and others added 2 commits May 18, 2026 15:55
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>
@grokys grokys marked this pull request as ready for review May 20, 2026 08:40
@grokys grokys marked this pull request as draft May 20, 2026 08:41
grokys and others added 3 commits May 20, 2026 10:54
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>
@grokys grokys marked this pull request as ready for review May 20, 2026 09:45
@grokys
Copy link
Copy Markdown
Member Author

grokys commented May 20, 2026

Tested with all Pro components and S3 upload implemented. Ready for review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread .github/workflows/source-release.yml
Comment thread .github/workflows/source-release.yml
Comment thread scripts/source-release/stage.sh Outdated
- 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants