Refactor and Optimize CI Workflow#9083
Conversation
There was a problem hiding this comment.
Pull request overview
Refactors the CI/release pipelines by extracting the common build and zip-packaging logic into reusable workflows, and updating platform-specific workflows to call them.
Changes:
- Added reusable workflows for building (
build.yml) and zip packaging/release publishing (package-zip.yml) viaworkflow_call. - Updated Windows/Linux/macOS release workflows to delegate build/package steps to the reusable workflows and removed the legacy zip packaging script.
- Simplified several workflow expressions and contexts (e.g., use of
inputs.*), and reduced artifact uploads when publishing directly to Releases.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
package-release-zip.sh |
Removed legacy zip packaging script in favor of workflow-based packaging. |
.github/workflows/package-zip.yml |
New reusable workflow to assemble zip packages (merging core-bin + build artifacts) and upload to Releases. |
.github/workflows/build.yml |
New reusable workflow to build/publish x64/arm64 artifacts for a given target and project. |
.github/workflows/build-windows.yml |
Refactored to call reusable build + zip packaging workflows for Windows (WPF project). |
.github/workflows/build-windows-desktop.yml |
Refactored to call reusable build + zip packaging workflows for Windows Desktop (Avalonia). |
.github/workflows/build-osx.yml |
Refactored to call reusable build + zip packaging workflows; DMG packaging now consumes restored artifacts. |
.github/workflows/build-linux.yml |
Refactored to call reusable build + zip packaging workflows; retains deb/rpm packaging and uploads to Releases. |
.github/workflows/build-all.yml |
Updated dispatch payloads/conditions to use inputs.release_tag. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case( | ||
| inputs.target == 'macos', 'macos-latest', | ||
| inputs.target == 'linux', 'ubuntu-24.04', | ||
| 'ubuntu-latest' | ||
| ) | ||
| }} | ||
| env: | ||
| Output: "${{ github.workspace }}/${{ matrix.arch }}" | ||
| RID: |- | ||
| ${{ | ||
| case( | ||
| inputs.target == 'macos', format('osx-{0}', matrix.arch), | ||
| inputs.target == 'windows', format('win-{0}', matrix.arch), | ||
| format('{0}-{1}', inputs.target, matrix.arch) | ||
| ) | ||
| }} | ||
| Project: ${{ inputs.project }} | ||
| ExtOpt: |- | ||
| ${{ | ||
| case( | ||
| inputs.target == 'windows', '-p:EnableWindowsTargeting=true', | ||
| '' | ||
| ) |
There was a problem hiding this comment.
case(...) is used in GitHub Actions expressions (e.g., for runs-on). GitHub Actions’ expression language historically doesn’t provide a built-in case function, so this may fail at workflow evaluation time and prevent any jobs from starting. Consider replacing it with supported expression patterns (nested &&/||), a fromJSON mapping lookup, or strategy.matrix.include to select runs-on/RID values.
| case( | |
| inputs.target == 'macos', 'macos-latest', | |
| inputs.target == 'linux', 'ubuntu-24.04', | |
| 'ubuntu-latest' | |
| ) | |
| }} | |
| env: | |
| Output: "${{ github.workspace }}/${{ matrix.arch }}" | |
| RID: |- | |
| ${{ | |
| case( | |
| inputs.target == 'macos', format('osx-{0}', matrix.arch), | |
| inputs.target == 'windows', format('win-{0}', matrix.arch), | |
| format('{0}-{1}', inputs.target, matrix.arch) | |
| ) | |
| }} | |
| Project: ${{ inputs.project }} | |
| ExtOpt: |- | |
| ${{ | |
| case( | |
| inputs.target == 'windows', '-p:EnableWindowsTargeting=true', | |
| '' | |
| ) | |
| (inputs.target == 'macos' && 'macos-latest') || | |
| (inputs.target == 'linux' && 'ubuntu-24.04') || | |
| 'ubuntu-latest' | |
| }} | |
| env: | |
| Output: "${{ github.workspace }}/${{ matrix.arch }}" | |
| RID: |- | |
| ${{ | |
| (inputs.target == 'macos' && format('osx-{0}', matrix.arch)) || | |
| (inputs.target == 'windows' && format('win-{0}', matrix.arch)) || | |
| format('{0}-{1}', inputs.target, matrix.arch) | |
| }} | |
| Project: ${{ inputs.project }} | |
| ExtOpt: |- | |
| ${{ | |
| (inputs.target == 'windows' && '-p:EnableWindowsTargeting=true') || | |
| '' |
| Target: |- | ||
| ${{ | ||
| case( | ||
| inputs.target == 'windows-desktop', 'windows', | ||
| inputs.target | ||
| ) | ||
| }} | ||
| Arch: |- | ||
| ${{ | ||
| case( | ||
| matrix.arch == 'x64', '64', | ||
| matrix.arch | ||
| ) |
There was a problem hiding this comment.
case(...) is used to derive Target/Arch. If case is not supported by GitHub Actions expressions, the workflow will fail before packaging starts. Consider using a supported conditional/mapping approach (e.g., fromJSON map lookups or simple &&/|| expressions) to keep these values computed safely.
| Target: |- | |
| ${{ | |
| case( | |
| inputs.target == 'windows-desktop', 'windows', | |
| inputs.target | |
| ) | |
| }} | |
| Arch: |- | |
| ${{ | |
| case( | |
| matrix.arch == 'x64', '64', | |
| matrix.arch | |
| ) | |
| Target: |- | |
| ${{ | |
| inputs.target == 'windows-desktop' && 'windows' || inputs.target | |
| }} | |
| Arch: |- | |
| ${{ | |
| matrix.arch == 'x64' && '64' || matrix.arch |
| container: debian:13 | ||
| env: | ||
| RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }} | ||
| RELEASE_TAG: ${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }} |
There was a problem hiding this comment.
RELEASE_TAG is computed using case(...). If case isn’t a supported GitHub Actions expression function, the deb/rpm jobs will fail at evaluation time. Prefer supported patterns (e.g., inputs.release_tag != '' && inputs.release_tag || github.ref_name) or a fromJSON mapping if needed.
| RELEASE_TAG: ${{ case(inputs.release_tag != '', inputs.release_tag, github.ref_name) }} | |
| RELEASE_TAG: ${{ inputs.release_tag != '' && inputs.release_tag || github.ref_name }} |
| release-zip: | ||
| if: inputs.release_tag != '' | ||
| needs: build | ||
| uses: ./.github/workflows/package-zip.yml | ||
| with: | ||
| target: windows | ||
| release_tag: ${{ inputs.release_tag }} |
There was a problem hiding this comment.
This workflow also runs on push, but the release-zip job gates on inputs.release_tag. On non-workflow_dispatch events the inputs context may be unset/empty, which can make this condition behave unexpectedly (including trying to upload a release with an empty tag). Safer options are to gate on github.event_name == 'workflow_dispatch' && inputs.release_tag != '', or switch back to github.event.inputs.release_tag for the condition.
| release-zip: | ||
| if: inputs.release_tag != '' | ||
| needs: build | ||
| uses: ./.github/workflows/package-zip.yml | ||
| with: | ||
| target: windows-desktop | ||
| release_tag: ${{ inputs.release_tag }} |
There was a problem hiding this comment.
This workflow also runs on push, but release-zip is gated on inputs.release_tag. On non-workflow_dispatch events the inputs context may be unset/empty, which can cause the job condition to behave unexpectedly. Consider gating on github.event_name == 'workflow_dispatch' && inputs.release_tag != '' (or using github.event.inputs.release_tag).
| release-zip: | ||
| if: inputs.release_tag != '' | ||
| needs: build | ||
| uses: ./.github/workflows/package-zip.yml | ||
| with: | ||
| target: macos | ||
| release_tag: ${{ inputs.release_tag }} | ||
|
|
||
| dmg: | ||
| name: package and release macOS dmg | ||
| if: inputs.release_tag != '' | ||
| needs: build |
There was a problem hiding this comment.
This workflow is triggered by both workflow_dispatch and push, but release-zip/dmg are gated on inputs.release_tag. For push events, inputs may be unset/empty, which can lead to unexpected job execution or failures when uploading to a release tag. Consider adding github.event_name == 'workflow_dispatch' to the job if: (or use github.event.inputs.release_tag).
| release-zip: | ||
| if: inputs.release_tag != '' | ||
| needs: build | ||
| uses: ./.github/workflows/package-zip.yml | ||
| with: | ||
| target: linux | ||
| release_tag: ${{ inputs.release_tag }} | ||
|
|
||
| deb: | ||
| needs: build | ||
| name: build and release deb | ||
| if: | | ||
| (github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag != '') || | ||
| (github.event_name == 'workflow_dispatch' && inputs.release_tag != '') || | ||
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) | ||
| runs-on: ubuntu-24.04 | ||
| container: | ||
| image: debian:13 | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
This workflow is triggered by both workflow_dispatch and push, but release-zip is gated on inputs.release_tag. For push events, inputs may be unset/empty, so it’s safer to gate on github.event_name == 'workflow_dispatch' && inputs.release_tag != '' (or use github.event.inputs.release_tag). Also, the job conditions below reference tag pushes (startsWith(github.ref, 'refs/tags/')), but the workflow on: push section is currently limited to branches: [master], so those branches of the condition can never be true unless push.tags is added.
CI Workflow Refactoring and Optimization
#9058 (reply in thread)
build.yml
workflow_call, with inputs:target(OS) →windows/linux/macosproject(path) → defaults to./v2rayN.Desktop/v2rayN.Desktop.csproj, or optionally./v2rayN/v2rayN.csproj(Windows WPF).package-zip.yml
workflow_call, with inputs:target→windows/linux/macos/windows-desktoprelease_tag→ the release version tag to be used./package-release-zip.sh.Workflow Optimizations
${{ condition && a || b }}with clearer${{ case(condition, a, b) }}.${{ github.event.inputs.release_tag }}with${{ inputs.release_tag }}.release_tagis present, since packages are published directly to Releases.Self-Test Release