Skip to content

feat(cfc): execution control (EN/ENO) #5092

feat(cfc): execution control (EN/ENO)

feat(cfc): execution control (EN/ENO) #5092

Workflow file for this run

name: Build Linux
on:
push:
branches: [master, "release/[0-9]*"]
pull_request:
branches: [master, "release/[0-9]*"]
merge_group:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
env:
IMAGE_NAME: rusty
jobs:
check:
name: Check
runs-on: ubuntu-latest
container: ghcr.io/plc-lang/rust-llvm:21-1.95
steps:
- uses: actions/checkout@v6
- name: Run Check
run: |
./scripts/build.sh --check
test-linux:
name: Test Linux
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config:
- { os: "ubuntu-latest" }
- { os: "ubuntu-24.04-arm" }
steps:
- name: Free up disk space on runner
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Show disk space after cleanup
run: df -h
- uses: actions/checkout@v6
- name: Cargo test
run: |
docker run --rm \
--entrypoint /bin/bash \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
ghcr.io/plc-lang/rust-llvm:21-1.95 \
-c "df -h && ./scripts/build.sh --build --test"
test-linux-release:
name: Test Linux (release)
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config:
- { os: "ubuntu-latest" }
- { os: "ubuntu-24.04-arm" }
steps:
- name: Free up disk space on runner
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Show disk space after cleanup
run: df -h
- uses: actions/checkout@v6
- name: Cargo test (release)
run: |
docker run --rm \
--entrypoint /bin/bash \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
ghcr.io/plc-lang/rust-llvm:21-1.95 \
-c "df -h && ./scripts/build.sh --build --test --release"
package-linux:
name: Package Linux (${{ matrix.config.arch }})
runs-on: ${{ matrix.config.os }}
strategy:
matrix:
config:
- { os: "ubuntu-latest", arch: "x86_64" }
- { os: "ubuntu-24.04-arm", arch: "aarch64" }
container: ghcr.io/plc-lang/rust-llvm:21-1.95
steps:
- uses: actions/checkout@v6
- name: Release Build (native ${{ matrix.config.arch }})
shell: bash
run: |
echo "Building native binary for ${{ matrix.config.arch }}"
./scripts/build.sh --build --release --package --deb
- name: Build and package the std lib
if: matrix.config.arch == 'x86_64'
shell: bash
run: |
# required by aarch64 build
apt update && apt install -y gcc-aarch64-linux-gnu
# Clean up apt cache to free disk space
apt-get clean
rm -rf /var/lib/apt/lists/*
echo "Build command : ./scripts/build.sh --build --release"
./scripts/build.sh --build --release --package --deb \
--target x86_64-linux-gnu,aarch64-linux-gnu,x86_64-unknown-linux-gnu,aarch64-unknown-linux-gnu
- uses: actions/upload-artifact@v7
with:
name: plc-${{ matrix.config.arch }}
path: target/release/plc
if-no-files-found: error
- uses: actions/upload-artifact@v7
if: matrix.config.arch == 'x86_64'
with:
name: stdlib
path: output/
- uses: actions/upload-artifact@v7
if: matrix.config.arch == 'x86_64'
with:
name: schema
path: compiler/plc_project/schema/
- uses: actions/upload-artifact@v7
with:
name: deb-${{ matrix.config.arch }}
path: target/debian/*.deb
if-no-files-found: error
build-and-push-image:
name: Build Image (${{ matrix.config.arch }})
runs-on: ${{ matrix.config.os }}
needs: package-linux
strategy:
matrix:
config:
- { os: "ubuntu-latest", arch: "x86_64", docker_arch: "amd64" }
- { os: "ubuntu-24.04-arm", arch: "aarch64", docker_arch: "arm64" }
permissions:
actions: read
packages: write
contents: read
steps:
- uses: actions/checkout@v6
- name: Download deb artifacts (x86_64)
uses: actions/download-artifact@v8
with:
name: deb-x86_64
path: artifacts/deb-x86_64/
- name: Download deb artifacts (aarch64)
uses: actions/download-artifact@v8
with:
name: deb-aarch64
path: artifacts/deb-aarch64/
- name: Prepare deb artifacts
run: |
mkdir -p artifacts/deb
# Debug: show artifact structure
echo "Artifact structure:"
find artifacts/ -name "*.deb" -type f
# Copy the plc binary deb for this image's architecture only
find artifacts/deb-${{ matrix.config.arch }}/ -name "plc-compiler_*_${{ matrix.config.docker_arch }}.deb" -exec cp {} artifacts/deb/ \;
# Copy plc-stdlib debs for BOTH architectures (cross-compilation support)
find artifacts/deb-x86_64/ -name "plc-stdlib_*_amd64.deb" -exec cp {} artifacts/deb/ \;
find artifacts/deb-aarch64/ -name "plc-stdlib_*_arm64.deb" -exec cp {} artifacts/deb/ \;
# Validate that expected files were copied
echo "Deb packages for Docker image (${{ matrix.config.docker_arch }}):"
ls -la artifacts/deb/
if [ -z "$(ls -A artifacts/deb/*.deb 2>/dev/null)" ]; then
echo "ERROR: No deb files were copied to artifacts/deb/"
exit 1
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build Docker image
run: |
docker buildx build . \
--platform linux/${{ matrix.config.docker_arch }} \
--file Dockerfile \
--tag ${{ env.IMAGE_NAME }}:local \
--load
- name: Log in to registry
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name != 'pull_request' && github.ref == 'refs/heads/master')
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push image with temp tag
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name != 'pull_request' && github.ref == 'refs/heads/master')
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
TEMP_TAG="build-${{ github.run_id }}-${{ matrix.config.arch }}"
docker tag ${{ env.IMAGE_NAME }}:local $IMAGE_ID:$TEMP_TAG
docker push $IMAGE_ID:$TEMP_TAG
echo "Pushed: $IMAGE_ID:$TEMP_TAG"
- name: Simulate push (PR only)
if: ${{ github.event_name == 'pull_request' }}
run: |
echo "=========================================="
echo "SIMULATION MODE (Pull Request)"
echo "Would push: ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:build-${{ github.run_id }}-${{ matrix.config.arch }}"
echo "Platform: linux/${{ matrix.config.docker_arch }}"
echo "=========================================="
push-multiplatform:
name: Push Multiplatform Manifest
needs: build-and-push-image
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name != 'pull_request' && github.ref == 'refs/heads/master')
permissions:
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
echo "Creating multiplatform manifest..."
docker buildx imagetools create \
-t $IMAGE_ID:latest \
$IMAGE_ID:build-${{ github.run_id }}-x86_64 \
$IMAGE_ID:build-${{ github.run_id }}-aarch64
echo "Pushed: $IMAGE_ID:latest"
- name: Cleanup temp tags
if: always()
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Delete temp images by tag
for ARCH in x86_64 aarch64; do
TEMP_TAG="build-${{ github.run_id }}-$ARCH"
echo "Deleting $TEMP_TAG..."
# Get version ID for this tag
VERSION_ID=$(gh api \
-H "Accept: application/vnd.github+json" \
"/orgs/${{ github.repository_owner }}/packages/container/${{ env.IMAGE_NAME }}/versions" \
--jq ".[] | select(.metadata.container.tags[] == \"$TEMP_TAG\") | .id")
if [ -n "$VERSION_ID" ]; then
gh api --method DELETE \
-H "Accept: application/vnd.github+json" \
"/orgs/${{ github.repository_owner }}/packages/container/${{ env.IMAGE_NAME }}/versions/$VERSION_ID" || true
echo "Deleted version $VERSION_ID"
fi
done
echo "Cleanup complete"
style:
name: Check Style
runs-on: ubuntu-latest
container: ghcr.io/plc-lang/rust-llvm:21-1.95
steps:
- uses: actions/checkout@v6
- name: Run Checks
run: |
./scripts/build.sh --check-style
coverage:
name: Run Coverage
runs-on: ubuntu-latest
steps:
- name: Free up disk space on runner
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: false
swap-storage: true
- name: Show disk space after cleanup
run: df -h
- uses: actions/checkout@v6
- name: Run coverage in container
run: |
docker run --rm \
--entrypoint /bin/bash \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
ghcr.io/plc-lang/rust-llvm:21-1.95 \
-c "df -h && apt-get clean && rm -rf /var/lib/apt/lists/* && ./scripts/build.sh --coverage"
- name: Upload to codecov.io
uses: codecov/codecov-action@v6
with:
files: lcov.info
- name: Archive code coverage results
uses: actions/upload-artifact@v7
with:
name: code-coverage-report
path: lcov.info
pr-artifacts:
name: PR Artifacts
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
needs: package-linux
permissions:
pull-requests: write
steps:
- uses: actions/github-script@v9
with:
script: |
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
});
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const lines = artifacts.artifacts
.filter(a => a.name !== 'code-coverage-report')
.map(a => `| \`${a.name}\` | [Download](${runUrl}/artifacts/${a.id}) | ${(a.size_in_bytes / 1024 / 1024).toFixed(1)} MB |`);
const section = [
'#### :penguin: Linux',
'| Artifact | Link | Size |',
'|----------|------|------|',
...lines,
'',
`_From [workflow run](${runUrl})_`,
].join('\n');
const COMMENT_MARKER = '<!-- pr-artifacts -->';
const SECTION_START = '<!-- linux-start -->';
const SECTION_END = '<!-- linux-end -->';
const sectionBlock = `${SECTION_START}\n${section}\n${SECTION_END}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body.includes(COMMENT_MARKER));
if (existing) {
const re = new RegExp(`${SECTION_START}[\\s\\S]*?${SECTION_END}`);
const body = re.test(existing.body)
? existing.body.replace(re, sectionBlock)
: existing.body + '\n\n' + sectionBlock;
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `${COMMENT_MARKER}\n### Build Artifacts\n\n${sectionBlock}`,
});
}