diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 17643a5b..96582637 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,5 @@ -name: CUPS Snap CI - Native Build Pipeline +name: CUPS Snap CI - Unified Build Pipeline + on: push: branches: @@ -8,49 +9,51 @@ on: - master workflow_dispatch: +permissions: + contents: read + pull-requests: write # Added this so the bot can post the PR comment jobs: - build-snap: - name: Build CUPS Snap (${{ matrix.arch }}) + # Job 1: Native builds for amd64 and arm64 + build-native: + name: Build Native (${{ matrix.arch }}) runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false matrix: include: - # x86_64 native build on standard Ubuntu runner - - arch: x86_64 + - arch: amd64 runs-on: ubuntu-latest - - # arm64 native build on ARM64-capable runner - arch: arm64 runs-on: ubuntu-24.04-arm steps: - name: Checkout cups-snap sources uses: actions/checkout@v4 + - name: Print Ubuntu version run: | set -e cat /etc/os-release | grep PRETTY_NAME | awk -F '=' '{print $2}' + - name: Build CUPS snap with Snapcraft action id: snapcraft uses: snapcore/action-build@v1 with: path: . + - name: Install built snap (smoke test setup) run: | set -e SNAP_FILE="${{ steps.snapcraft.outputs.snap }}" echo "Installing snap: ${SNAP_FILE}" sudo snap install --dangerous "${SNAP_FILE}" + - name: Run smoke tests run: | set -e - - # Verify snap is listed echo "Checking snap list..." snap list | grep "^cups " || (echo "CUPS snap not found in snap list"; exit 1) - # Test ghostscript binary with -h flag echo "Testing ghostscript binary..." snap run cups.gs -h || true @@ -65,4 +68,74 @@ jobs: with: name: cups-snap-${{ matrix.arch }} path: ./*.snap - if-no-files-found: error \ No newline at end of file + if-no-files-found: error + + # Job 2: Remote build via Launchpad for armhf + # Job 2: Remote build via Launchpad for armhf + build-remote: + name: Build Remote (armhf) + runs-on: ubuntu-latest + steps: + - name: Checkout cups-snap sources + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check Launchpad Credentials & Scenarios + id: check-secrets + env: + LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }} + run: | + if [ -z "$LP_CREDENTIALS" ]; then + if [ "${{ github.event_name }}" == "pull_request" ]; then + echo "available=false" >> $GITHUB_OUTPUT + echo "::notice title=Launchpad CI Bypassed::GitHub Actions cannot securely access repository secrets in PRs from external forks. Bypassing the armhf remote build so it does not falsely block your merge. The compilation will be verified automatically in the push workflow immediately after merging into master." + else + echo "available=false" >> $GITHUB_OUTPUT + echo "::error::No secrets found. LP_CREDENTIALS is missing from the repository." + exit 1 + fi + else + echo "available=true" >> $GITHUB_OUTPUT + fi + + - name: Install Snapcraft + if: steps.check-secrets.outputs.available == 'true' + run: | + sudo snap install snapcraft --channel latest/stable --classic + + - name: Setup Launchpad credentials + if: steps.check-secrets.outputs.available == 'true' + env: + LP_CREDENTIALS: ${{ secrets.LP_CREDENTIALS }} + run: | + set -e + mkdir -p ~/.local/share/snapcraft/provider/launchpad ~/.local/share/snapcraft + echo "$LP_CREDENTIALS" > ~/.local/share/snapcraft/provider/launchpad/credentials + echo "$LP_CREDENTIALS" > ~/.local/share/snapcraft/launchpad-credentials + + - name: Remote build CUPS snap on Launchpad + if: steps.check-secrets.outputs.available == 'true' + run: | + set +e # Disable auto-exit to capture output + OUTPUT=$(snapcraft remote-build --launchpad-accept-public-upload --build-for=armhf 2>&1) + EXIT_CODE=$? + set -e # Re-enable auto-exit + + if [ $EXIT_CODE -ne 0 ]; then + echo "$OUTPUT" # Print the full log for debugging + if echo "$OUTPUT" | grep -iqE "macaroon|unauthorized|authentication|login|credentials"; then + echo "::error::Secrets are invalid or expired. Launchpad authentication failed." + else + echo "::error::Launchpad build failed due to a code compilation error or infrastructure issue." + fi + exit $EXIT_CODE + fi + + - name: Upload CUPS snap artifact + if: steps.check-secrets.outputs.available == 'true' + uses: actions/upload-artifact@v4 + with: + name: cups-snap-armhf + path: ./*.snap + if-no-files-found: error diff --git a/README.md b/README.md index eb25ce56..75c7f0e3 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,79 @@ This repository uses a custom GitHub Actions workflow for CodeQL static analysis *Note: If the Default setup is active, GitHub may reject the results uploaded by the manual workflow, causing the CI job to fail.* +## Launchpad Credentials for armhf Remote Build + +The `armhf` build in CI uses **Snapcraft remote-build with Launchpad**. +Since GitHub runners do not have a Launchpad identity, authentication credentials must be generated locally and stored as a **GitHub repository secret**. + +Follow the steps below to generate and configure the credentials. + +### 1. Generate Launchpad Credentials +The credentials are generated via an interactive **Snapcraft OAuth login**. +First navigate to the project directory: +``` +cd path/to/cups-snap +``` +Run the following command: +```bash +snapcraft remote-build +``` +Snapcraft will display a Launchpad authorization URL. +- Copy the URL shown in the terminal. +- Open it in your browser. +- Click Authorize. +- Return to the terminal. +- Once the upload starts, stop the process using: + ``` + Ctrl + C + ``` +Stopping the process here is expected. The credentials will already have been generated. + +### 2. Extract the Credentials +Snapcraft stores the generated credentials locally. +Run: +``` +cat ~/.local/share/snapcraft/provider/launchpad/credentials +``` +If the file is not present, check: +``` +cat ~/.local/share/snapcraft/launchpad-credentials +``` +You will see output similar to: +``` +[1] +consumer_key = System-wide: Ubuntu (...) +consumer_secret = +access_token = +access_secret = +``` +Copy the entire block, including [1]. + +### 3. Store the Credentials in GitHub Secrets +The credentials must be added as a repository secret. +Navigate to: +``` +Repository → Settings → Secrets and variables → Actions +``` +Create a new secret: +``` +LP_CREDENTIALS +``` +Value: +Paste the complete credential block copied in the previous step. + +Example: +``` +[1] +consumer_key = System-wide: Ubuntu (...) +consumer_secret = +access_token = XXXXX +access_secret = XXXXX +``` +These credentials will then be used automatically by the CI workflow when performing the armhf remote build. + +*Note: Launchpad OAuth credentials generated by Snapcraft expire after approximately one year.If the CI workflow begins failing with authentication or authorization errors, new credentials must be generated by repeating the steps above and updating the LP_CREDENTIALS repository secret.* + ## Discussion and Links Call for testing: diff --git a/snapcraft.yaml b/snapcraft.yaml index 3e8c08cd..289fc1be 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,6 +1,6 @@ name: cups base: core22 # The base Snap is the execution environment for this Snap -version: '2.4.16-1' +version: '2.4.16-2' grade: stable summary: CUPS-based printing stack Snap description: Complete printing environment in a Snap @@ -12,11 +12,16 @@ adopt-info: cups assumes: [snapd2.55] # Only build on the architectures supported +# Explicit architecture mapping to support both native runners and Launchpad remote-build architectures: - build-on: amd64 + build-for: amd64 - build-on: arm64 + build-for: arm64 - build-on: armhf + build-for: armhf - build-on: riscv64 + build-for: riscv64 # System user for filters and backends to drop privileges, "lp" is not # available in a Snap @@ -339,6 +344,9 @@ parts: - --with-cups-serverroot=/var/snap/cups/common/etc/cups - --with-cups-datadir=/snap/cups/current/share/cups build-environment: + # Universal fixes: Forces C99 standard, compatible across all architectures. + - CFLAGS: "-std=gnu99 -Wno-error=declaration-after-statement" + - CXXFLAGS: "-std=gnu++99" # To find the libraries built in this Snap - LD_LIBRARY_PATH: "${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$CRAFT_STAGE/usr/lib:$CRAFT_STAGE/usr/lib/${CRAFT_ARCH_TRIPLET_BUILD_FOR}:$CRAFT_STAGE/lib" stage-packages: @@ -594,7 +602,8 @@ parts: source: scripts/ override-build: | set -eux - gcc -o port-occupied port-occupied.c + # Uses CRAFT_ARCH_TRIPLET_BUILD_FOR to gracefully support all architectures natively and via remote + "${CRAFT_ARCH_TRIPLET_BUILD_FOR}-gcc" -o port-occupied port-occupied.c craftctl default organize: run-cupsd: scripts/run-cupsd