mygithub.libinneed.workers.dev/stackitcloud/stackit-cli
Advanced tools
| #!/usr/bin/env bash | ||
| # This script is used to publish new RPM packages to the CLI RPM repository | ||
| # Usage: ./publish-rpm-packages.sh | ||
| set -eo pipefail | ||
| PACKAGES_BUCKET_URL="https://packages.stackit.cloud" | ||
| PUBLIC_KEY_FILE_PATH="keys/key.gpg" | ||
| RPM_REPO_PATH="rpm/cli" | ||
| RPM_BUCKET_NAME="distribution" | ||
| GORELEASER_PACKAGES_FOLDER="dist/" | ||
| # We need to disable the key database daemon (keyboxd) | ||
| # This can be done by removing "use-keyboxd" from ~/.gnupg/common.conf (see https://github.com/gpg/gnupg/blob/master/README) | ||
| echo -n >~/.gnupg/common.conf | ||
| # Create RPM repository directory structure | ||
| printf ">>> Creating RPM repository structure \n" | ||
| mkdir -p rpm-repo/x86_64 | ||
| mkdir -p rpm-repo/i386 | ||
| mkdir -p rpm-repo/aarch64 | ||
| # Copy RPM packages to appropriate architecture directories | ||
| printf "\n>>> Copying RPM packages to architecture directories \n" | ||
| # Copy x86_64 packages (amd64) | ||
| for rpm_file in "${GORELEASER_PACKAGES_FOLDER}"*_amd64.rpm; do | ||
| if [ -f "$rpm_file" ]; then | ||
| cp "$rpm_file" rpm-repo/x86_64/ | ||
| printf "Copied %s to x86_64/\n" "$(basename "$rpm_file")" | ||
| fi | ||
| done | ||
| # Copy i386 packages | ||
| for rpm_file in "${GORELEASER_PACKAGES_FOLDER}"*_386.rpm; do | ||
| if [ -f "$rpm_file" ]; then | ||
| cp "$rpm_file" rpm-repo/i386/ | ||
| printf "Copied %s to i386/\n" "$(basename "$rpm_file")" | ||
| fi | ||
| done | ||
| # Copy aarch64 packages (arm64) | ||
| for rpm_file in "${GORELEASER_PACKAGES_FOLDER}"*_arm64.rpm; do | ||
| if [ -f "$rpm_file" ]; then | ||
| cp "$rpm_file" rpm-repo/aarch64/ | ||
| printf "Copied %s to aarch64/\n" "$(basename "$rpm_file")" | ||
| fi | ||
| done | ||
| # Download existing repository content (RPMs and metadata) if it exists | ||
| printf "\n>>> Downloading existing repository content \n" | ||
| aws s3 sync s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/ rpm-repo/ --endpoint-url "${AWS_ENDPOINT_URL}" --exclude "*.asc" || echo "No existing repository found, creating new one" | ||
| # Create repository metadata for each architecture | ||
| printf "\n>>> Creating repository metadata \n" | ||
| for arch in x86_64 i386 aarch64; do | ||
| if [ -d "rpm-repo/${arch}" ] && [ -n "$(find "rpm-repo/${arch}" -mindepth 1 -maxdepth 1 -print -quit)" ]; then | ||
| printf "Creating metadata for %s...\n" "$arch" | ||
| # List what we're working with | ||
| file_list=$(find "rpm-repo/${arch}" -maxdepth 1 -type f -exec basename {} \; | tr '\n' ' ') | ||
| printf "Files in %s: %s\n" "$arch" "${file_list% }" | ||
| # Create repository metadata | ||
| createrepo_c --update rpm-repo/${arch} | ||
| # Sign the repository metadata | ||
| printf "Signing repository metadata for %s...\n" "$arch" | ||
| # Remove existing signature file if it exists | ||
| rm -f rpm-repo/${arch}/repodata/repomd.xml.asc | ||
| gpg --batch --pinentry-mode loopback --detach-sign --armor \ | ||
| --local-user "${GPG_PRIVATE_KEY_FINGERPRINT}" \ | ||
| --passphrase "${GPG_PASSPHRASE}" \ | ||
| rpm-repo/${arch}/repodata/repomd.xml | ||
| # Verify the signature was created | ||
| if [ -f "rpm-repo/${arch}/repodata/repomd.xml.asc" ]; then | ||
| printf "Repository metadata signed successfully for %s\n" "$arch" | ||
| else | ||
| printf "WARNING: Repository metadata signature not created for %s\n" "$arch" | ||
| fi | ||
| else | ||
| printf "No packages found for %s, skipping...\n" "$arch" | ||
| fi | ||
| done | ||
| # Upload the updated repository to S3 in two phases (repodata pointers last) | ||
| # clients reading the repo won't see a state where repomd.xml points to files not uploaded yet. | ||
| printf "\n>>> Uploading repository to S3 (phase 1: all except repomd*) \n" | ||
| aws s3 sync rpm-repo/ s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/ \ | ||
| --endpoint-url "${AWS_ENDPOINT_URL}" \ | ||
| --delete \ | ||
| --exclude "*/repodata/repomd.xml" \ | ||
| --exclude "*/repodata/repomd.xml.asc" | ||
| printf "\n>>> Uploading repository to S3 (phase 2: repomd* only) \n" | ||
| aws s3 sync rpm-repo/ s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/ \ | ||
| --endpoint-url "${AWS_ENDPOINT_URL}" \ | ||
| --exclude "*" \ | ||
| --include "*/repodata/repomd.xml" \ | ||
| --include "*/repodata/repomd.xml.asc" | ||
| # Upload the public key | ||
| # Also uploaded in APT publish; intentionally redundant | ||
| # Safe to overwrite and ensures updates if APT fails or key changes. | ||
| printf "\n>>> Uploading public key \n" | ||
| gpg --armor --export "${GPG_PRIVATE_KEY_FINGERPRINT}" > public-key.asc | ||
| aws s3 cp public-key.asc s3://${RPM_BUCKET_NAME}/${PUBLIC_KEY_FILE_PATH} --endpoint-url "${AWS_ENDPOINT_URL}" | ||
| printf "\n>>> RPM repository published successfully! \n" | ||
| printf "Repository URL: %s/%s/ \n" "$PACKAGES_BUCKET_URL" "$RPM_REPO_PATH" | ||
| printf "Public key URL: %s/%s \n" "$PACKAGES_BUCKET_URL" "$PUBLIC_KEY_FILE_PATH" |
@@ -44,2 +44,11 @@ # STACKIT CLI release workflow. | ||
| # nfpm-rpm signing needs gpg provided as filepath | ||
| # https://goreleaser.com/customization/nfpm/ | ||
| - name: Create GPG key file | ||
| run: | | ||
| KEY_PATH="$RUNNER_TEMP/gpg-private-key.asc" | ||
| printf '%s' "${{ secrets.GPG_PRIVATE_KEY }}" > "$KEY_PATH" | ||
| chmod 600 "$KEY_PATH" | ||
| echo "GPG_KEY_PATH=$KEY_PATH" >> "$GITHUB_ENV" | ||
| - name: Set up keychain | ||
@@ -75,4 +84,11 @@ run: | | ||
| GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| GPG_KEY_PATH: ${{ env.GPG_KEY_PATH }} | ||
| # nfpm-rpm signing needs this env to be set. | ||
| NFPM_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| # artifacts need to be passed to the "publish-apt" job somehow | ||
| - name: Clean up GPG key file | ||
| if: always() | ||
| run: | | ||
| rm -f "$GPG_KEY_PATH" | ||
| - name: Upload artifacts to workflow | ||
@@ -84,3 +100,3 @@ uses: actions/upload-artifact@v4 | ||
| retention-days: 1 | ||
| publish-apt: | ||
@@ -121,1 +137,40 @@ name: Publish APT | ||
| run: ./scripts/publish-apt-packages.sh | ||
| publish-rpm: | ||
| name: Publish RPM | ||
| runs-on: ubuntu-latest | ||
| needs: [goreleaser] | ||
| env: | ||
| # Needed to publish new packages to our S3-hosted RPM repo | ||
| AWS_ACCESS_KEY_ID: ${{ secrets.OBJECT_STORAGE_ACCESS_KEY_ID }} | ||
| AWS_SECRET_ACCESS_KEY: ${{ secrets.OBJECT_STORAGE_SECRET_ACCESS_KEY }} | ||
| AWS_DEFAULT_REGION: eu01 | ||
| AWS_ENDPOINT_URL: https://object.storage.eu01.onstackit.cloud | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| - name: Download artifacts from workflow | ||
| uses: actions/download-artifact@v5 | ||
| with: | ||
| name: goreleaser-dist-temp | ||
| path: dist | ||
| - name: Install RPM tools | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y createrepo-c | ||
| - name: Import GPG key | ||
| uses: crazy-max/ghaction-import-gpg@v6 | ||
| id: import_gpg | ||
| with: | ||
| gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | ||
| passphrase: ${{ secrets.GPG_PASSPHRASE }} | ||
| - name: Publish RPM packages | ||
| if: contains(github.ref_name, '-') == false | ||
| env: | ||
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| GPG_PRIVATE_KEY_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| run: ./scripts/publish-rpm-packages.sh |
+4
-11
@@ -102,13 +102,6 @@ version: 2 | ||
| signs: | ||
| - artifacts: package | ||
| args: | ||
| [ | ||
| "-u", | ||
| "{{ .Env.GPG_FINGERPRINT }}", | ||
| "--output", | ||
| "${signature}", | ||
| "--detach-sign", | ||
| "${artifact}", | ||
| ] | ||
| rpm: | ||
| # The package is signed if a key_file is set | ||
| signature: | ||
| key_file: "{{ .Env.GPG_KEY_PATH }}" | ||
@@ -115,0 +108,0 @@ homebrew_casks: |
+44
-6
@@ -133,14 +133,52 @@ # Installation | ||
| #### RPM package via dnf, yum and zypper | ||
| #### RHEL/Fedora/Rocky/Alma/openSUSE/... (`DNF/YUM/Zypper`) | ||
| The STACKIT CLI is available as [RPM Package](https://github.com/stackitcloud/stackit-cli/releases) and can be installed via dnf, yum and zypper package manager. | ||
| The STACKIT CLI can be installed through the [`DNF/YUM`](https://docs.fedoraproject.org/en-US/fedora/f40/system-administrators-guide/package-management/DNF/) / [`Zypper`](https://de.opensuse.org/Zypper) package managers. | ||
| Just download the rpm package from the [release page](https://github.com/stackitcloud/stackit-cli/releases) and run the install command like the following: | ||
| > Requires rpm version 4.15 or newer to support Ed25519 signatures. | ||
| > `$basearch` is supported by modern distributions. On older systems that don't expand `$basearch`, replace it in the `baseurl` with your architecture explicitly (for example, `.../rpm/cli/x86_64` or `.../rpm/cli/aarch64`). | ||
| ##### Installation via DNF/YUM | ||
| 1. Add the repository: | ||
| ```shell | ||
| dnf install stackitcli.rpm | ||
| yum install stackitcli.rpm | ||
| zypper install stackitcli.rpm | ||
| sudo tee /etc/yum.repos.d/stackit.repo > /dev/null << 'EOF' | ||
| [stackit] | ||
| name=STACKIT CLI | ||
| baseurl=https://packages.stackit.cloud/rpm/cli/$basearch | ||
| enabled=1 | ||
| gpgcheck=1 | ||
| gpgkey=https://packages.stackit.cloud/keys/key.gpg | ||
| EOF | ||
| ``` | ||
| 2. Install the CLI: | ||
| ```shell | ||
| sudo dnf install stackit | ||
| ``` | ||
| ##### Installation via Zypper | ||
| 1. Add the repository: | ||
| ```shell | ||
| sudo tee /etc/zypp/repos.d/stackit.repo > /dev/null << 'EOF' | ||
| [stackit] | ||
| name=STACKIT CLI | ||
| baseurl=https://packages.stackit.cloud/rpm/cli/$basearch | ||
| enabled=1 | ||
| gpgcheck=1 | ||
| gpgkey=https://packages.stackit.cloud/keys/key.gpg | ||
| EOF | ||
| ``` | ||
| 2. Install the CLI: | ||
| ```shell | ||
| sudo zypper install stackit | ||
| ``` | ||
| #### Any distribution | ||
@@ -147,0 +185,0 @@ |
@@ -52,2 +52,2 @@ #!/usr/bin/env bash | ||
| printf "\n>>> Publishing updated snapshot \n" | ||
| aptly publish snapshot -keyring="${CUSTOM_KEYRING_FILE}" -gpg-key="${GPG_PRIVATE_KEY_FINGERPRINT}" -passphrase "${GPG_PASSPHRASE}" -config "${APTLY_CONFIG_FILE_PATH}" updated-snapshot "s3:${APT_BUCKET_NAME}:${APT_REPO_PATH}" | ||
| aptly publish snapshot -keyring="${CUSTOM_KEYRING_FILE}" -gpg-key="${GPG_PRIVATE_KEY_FINGERPRINT}" -passphrase "${GPG_PASSPHRASE}" -config "${APTLY_CONFIG_FILE_PATH}" updated-snapshot "s3:${APT_BUCKET_NAME}:${APT_REPO_PATH}" |