mygithub.libinneed.workers.dev/stackitcloud/stackit-cli
Advanced tools
@@ -20,8 +20,9 @@ # STACKIT CLI release workflow. | ||
| goreleaser: | ||
| name: Build and Release | ||
| name: Release | ||
| runs-on: macOS-latest | ||
| outputs: | ||
| gpg_fingerprint: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| env: | ||
| SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }} | ||
| # Needed to publish new packages to our S3-hosted APT repo | ||
| AWS_ACCESS_KEY_ID: ${{ secrets.OBJECT_STORAGE_ACCESS_KEY_ID }} | ||
| AWS_SECRET_ACCESS_KEY: ${{ secrets.OBJECT_STORAGE_SECRET_ACCESS_KEY }} | ||
| steps: | ||
@@ -64,2 +65,7 @@ - name: Checkout | ||
| AUTHKEY_BASE64: ${{ secrets.APPLE_API_KEY }} | ||
| # aptly version 1.6.0 results in an segmentation fault. Therefore we fall back to version 1.5.0. | ||
| # Since it is not possible to specify a version via brew command a formula was added for aptly 1.5.0 | ||
| # (source: https://github.com/Homebrew/homebrew-core/pull/202415/files) | ||
| - name: Install Aptly version 1.5.0 | ||
| run: brew install aptly.rb | ||
| - name: Install Snapcraft | ||
@@ -74,51 +80,4 @@ uses: samuelmeuli/action-snapcraft@v3 | ||
| GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| - name: Upload dist artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: dist/ | ||
| retention-days: 1 | ||
| publish-packages: | ||
| name: Publish Packages | ||
| runs-on: ubuntu-latest | ||
| needs: goreleaser | ||
| if: contains(github.ref_name, '-') == false | ||
| env: | ||
| AWS_ACCESS_KEY_ID: ${{ secrets.OBJECT_STORAGE_ACCESS_KEY_ID }} | ||
| AWS_SECRET_ACCESS_KEY: ${{ secrets.OBJECT_STORAGE_SECRET_ACCESS_KEY }} | ||
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| GPG_PRIVATE_KEY_FINGERPRINT: ${{ needs.goreleaser.outputs.gpg_fingerprint }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - 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 }} | ||
| # aptly version 1.6.0 results in an segmentation fault. Therefore we fall back to version 1.5.0. | ||
| # Since it is not possible to specify a version via brew command a formula was added for aptly 1.5.0 | ||
| # (source: https://github.com/Homebrew/homebrew-core/pull/202415/files) | ||
| - name: Install Aptly version 1.5.0 | ||
| run: | | ||
| # Install aptly on Ubuntu | ||
| wget -O - https://www.aptly.info/pubkey.txt | sudo apt-key add - | ||
| echo "deb https://repo.aptly.info/ squeeze main" | sudo tee -a /etc/apt/sources.list.d/aptly.list | ||
| sudo apt-get update | ||
| sudo apt-get install -y aptly | ||
| - name: Install createrepo_c | ||
| run: | | ||
| # Install createrepo_c on Ubuntu | ||
| sudo apt-get update | ||
| sudo apt-get install -y createrepo-c | ||
| - name: Download dist artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: dist | ||
| path: dist/ | ||
| - name: Publish packages to APT repo | ||
| if: contains(github.ref_name, '-') == false | ||
| env: | ||
@@ -128,6 +87,1 @@ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| run: ./scripts/publish-apt-packages.sh | ||
| - name: Publish packages to RPM repo | ||
| env: | ||
| GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} | ||
| GPG_PRIVATE_KEY_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} | ||
| run: ./scripts/publish-rpm-packages.sh |
+2
-1
@@ -168,3 +168,4 @@ version: 2 | ||
| # if the tag has a prerelease indicator (e.g. v0.0.1-alpha1) | ||
| skip_upload: auto | ||
| # Temporarily not skipping prereleases to test integration with Winget | ||
| # skip_upload: auto | ||
| repository: | ||
@@ -171,0 +172,0 @@ owner: stackitcloud |
@@ -10,3 +10,3 @@ ## stackit ske kubeconfig create | ||
| By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created. | ||
| You can override this behavior by specifying a custom filepath with the --filepath flag. | ||
| You can override this behavior by specifying a custom filepath using the --filepath flag or by setting the KUBECONFIG env variable (fallback). | ||
@@ -51,3 +51,3 @@ An expiration time can be set for the kubeconfig. The expiration time is set in seconds(s), minutes(m), hours(h), days(d) or months(M). Default is 1h. | ||
| -e, --expiration string Expiration time for the kubeconfig in seconds(s), minutes(m), hours(h), days(d) or months(M). Example: 30d. By default, expiration time is 1h | ||
| --filepath string Path to create the kubeconfig file. By default, the kubeconfig is created as 'config' in the .kube folder, in the user's home directory. | ||
| --filepath string Path to create the kubeconfig file. Will fall back to KUBECONFIG env variable if not set. In case both aren't set, the kubeconfig is created as file named 'config' in the .kube folder in the user's home directory. | ||
| -h, --help Help for "stackit ske kubeconfig create" | ||
@@ -54,0 +54,0 @@ -l, --login Create a login kubeconfig that obtains valid credentials via the STACKIT CLI. This flag is mutually exclusive with the expiration flag. |
+1
-1
@@ -8,3 +8,3 @@ module github.com/stackitcloud/stackit-cli | ||
| github.com/goccy/go-yaml v1.18.0 | ||
| github.com/golang-jwt/jwt/v5 v5.2.3 | ||
| github.com/golang-jwt/jwt/v5 v5.3.0 | ||
| github.com/google/go-cmp v0.7.0 | ||
@@ -11,0 +11,0 @@ github.com/google/uuid v1.6.0 |
@@ -51,3 +51,3 @@ package create | ||
| "By default, the kubeconfig information of the SKE cluster is merged into the default kubeconfig file of the current user. If the kubeconfig file doesn't exist, a new one will be created.", | ||
| "You can override this behavior by specifying a custom filepath with the --filepath flag.\n", | ||
| "You can override this behavior by specifying a custom filepath using the --filepath flag or by setting the KUBECONFIG env variable (fallback).\n", | ||
| "An expiration time can be set for the kubeconfig. The expiration time is set in seconds(s), minutes(m), hours(h), days(d) or months(M). Default is 1h.\n", | ||
@@ -174,3 +174,3 @@ "Note that the format is <value><unit>, e.g. 30d for 30 days and you can't combine units."), | ||
| cmd.Flags().BoolP(loginFlag, "l", false, "Create a login kubeconfig that obtains valid credentials via the STACKIT CLI. This flag is mutually exclusive with the expiration flag.") | ||
| cmd.Flags().String(filepathFlag, "", "Path to create the kubeconfig file. By default, the kubeconfig is created as 'config' in the .kube folder, in the user's home directory.") | ||
| cmd.Flags().String(filepathFlag, "", "Path to create the kubeconfig file. Will fall back to KUBECONFIG env variable if not set. In case both aren't set, the kubeconfig is created as file named 'config' in the .kube folder in the user's home directory.") | ||
| cmd.Flags().StringP(expirationFlag, "e", "", "Expiration time for the kubeconfig in seconds(s), minutes(m), hours(h), days(d) or months(M). Example: 30d. By default, expiration time is 1h") | ||
@@ -177,0 +177,0 @@ cmd.Flags().Bool(overwriteFlag, false, "Overwrite the kubeconfig file.") |
@@ -701,1 +701,46 @@ package utils | ||
| } | ||
| func TestGetDefaultKubeconfigPathWithEnvVar(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| kubeconfigEnvVar string | ||
| expected string | ||
| userHome string | ||
| }{ | ||
| { | ||
| description: "base", | ||
| kubeconfigEnvVar: "~/.kube/custom/config", | ||
| expected: "~/.kube/custom/config", | ||
| userHome: "/home/test-user", | ||
| }, | ||
| { | ||
| description: "return user home when environment var is empty", | ||
| kubeconfigEnvVar: "", | ||
| expected: "/home/test-user/.kube/config", | ||
| userHome: "/home/test-user", | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| // Setup environment variables | ||
| err := os.Setenv("KUBECONFIG", tt.kubeconfigEnvVar) | ||
| if err != nil { | ||
| t.Errorf("could not set KUBECONFIG environment variable: %s", err) | ||
| } | ||
| err = os.Setenv("HOME", tt.userHome) | ||
| if err != nil { | ||
| t.Errorf("could not set HOME environment variable: %s", err) | ||
| } | ||
| output, err := GetDefaultKubeconfigPath() | ||
| if err != nil { | ||
| t.Errorf("failed on valid input") | ||
| } | ||
| if output != tt.expected { | ||
| t.Errorf("expected output to be %s, got %s", tt.expected, output) | ||
| } | ||
| }) | ||
| } | ||
| } |
@@ -287,4 +287,8 @@ package utils | ||
| // GetDefaultKubeconfigPath returns the default location for the kubeconfig file. | ||
| // GetDefaultKubeconfigPath returns the default location for the kubeconfig file or the value of KUBECONFIG if set. | ||
| func GetDefaultKubeconfigPath() (string, error) { | ||
| if kubeconfigEnv := os.Getenv("KUBECONFIG"); kubeconfigEnv != "" { | ||
| return kubeconfigEnv, nil | ||
| } | ||
| userHome, err := os.UserHomeDir() | ||
@@ -291,0 +295,0 @@ if err != nil { |
| #!/bin/bash | ||
| # This script is used to publish new packages to the CLI RPM repository | ||
| # Usage: ./publish-rpm-packages.sh | ||
| set -eo pipefail | ||
| ROOT_DIR=$(git rev-parse --show-toplevel) | ||
| PACKAGES_BUCKET_URL="https://packages.stackit.cloud" | ||
| RPM_REPO_PATH="rpm/cli" | ||
| RPM_BUCKET_NAME="distribution" | ||
| CUSTOM_KEYRING_FILE="rpm-keyring.gpg" | ||
| GORELEASER_PACKAGES_FOLDER="dist/" | ||
| TEMP_DIR=$(mktemp -d) | ||
| # 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 a local mirror of the current state of the remote RPM repository | ||
| printf ">>> Creating mirror \n" | ||
| curl ${PACKAGES_BUCKET_URL}/${RPM_REPO_PATH}/repodata/repomd.xml >${TEMP_DIR}/repomd.xml || echo "No existing repository found, creating new one" | ||
| # Create RPM repository structure | ||
| mkdir -p ${TEMP_DIR}/rpm-repo/RPMS | ||
| # Copy existing RPMs from remote repository (if any) | ||
| printf "\n>>> Downloading existing RPMs \n" | ||
| aws s3 sync s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/RPMS/ ${TEMP_DIR}/rpm-repo/RPMS/ --endpoint-url https://object.storage.eu01.onstackit.cloud || echo "No existing RPMs found" | ||
| # Copy new generated .rpm packages to the local repo | ||
| # Note: GoReleaser already signs these RPM packages with embedded signatures | ||
| printf "\n>>> Adding new packages to local repo \n" | ||
| cp ${GORELEASER_PACKAGES_FOLDER}/*.rpm ${TEMP_DIR}/rpm-repo/RPMS/ | ||
| # Create RPM repository metadata using createrepo_c | ||
| printf "\n>>> Creating RPM repository metadata \n" | ||
| createrepo_c ${TEMP_DIR}/rpm-repo | ||
| # Sign the repository metadata using the same GPG key as APT | ||
| if [ -n "$GPG_PRIVATE_KEY_FINGERPRINT" ] && [ -n "$GPG_PASSPHRASE" ]; then | ||
| printf "\n>>> Signing repository metadata \n" | ||
| gpg --batch --yes --pinentry-mode loopback --local-user="${GPG_PRIVATE_KEY_FINGERPRINT}" --passphrase="${GPG_PASSPHRASE}" --detach-sign --armor ${TEMP_DIR}/rpm-repo/repodata/repomd.xml | ||
| else | ||
| echo ">>> Skipping repository metadata signing (GPG environment variables not set)" | ||
| fi | ||
| # Upload to S3 | ||
| printf "\n>>> Uploading to S3 \n" | ||
| aws s3 sync ${TEMP_DIR}/rpm-repo/ s3://${RPM_BUCKET_NAME}/${RPM_REPO_PATH}/ --endpoint-url https://object.storage.eu01.onstackit.cloud | ||
| # Clean up | ||
| rm -rf ${TEMP_DIR} | ||
| printf "\n>>> RPM repository published successfully to ${PACKAGES_BUCKET_URL}/${RPM_REPO_PATH} \n" |
Sorry, the diff of this file is too big to display