Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c9a4b05
feat(ibmcloud): add GitHub Actions runner support for IBM Power and I…
deekay2310 Jun 9, 2026
9af3e13
fix: harden --ghactions-runner-image-repo input
deekay2310 Jun 9, 2026
f68b509
feat(ibmcloud): auto-generate GitHub Actions runner registration token
deekay2310 Jun 9, 2026
68f31f4
fix(ibmcloud): install prerequisites before runner image build
deekay2310 Jun 10, 2026
78d423b
ci: add s390x runner smoke test workflow
deekay2310 Jun 10, 2026
753b7ef
fix(ibmcloud): tolerate flaky upstream test failures in runner build
deekay2310 Jun 10, 2026
9e03696
fix(ibmcloud): preserve runner binary if post-build installer fails
deekay2310 Jun 11, 2026
7829062
revert: remove unnecessary runner binary watcher from snippets
deekay2310 Jun 11, 2026
a6893f0
fix(ibmcloud): run GitHub Actions runner as non-root user
deekay2310 Jun 11, 2026
60e93b8
fix(ibmcloud): repair PAM config after runner build to preserve sshd
deekay2310 Jun 11, 2026
ef87753
debug(ibmcloud): add sshd watchdog and diagnostic logging to ppc64le …
deekay2310 Jun 11, 2026
265345e
fix(ibmcloud): restore sshd privsep dir after runner build + COS diag…
deekay2310 Jun 11, 2026
de8fc1f
fix(ibmcloud): remove nonexistent /opt/dotnet from ppc64le snippet
deekay2310 Jun 11, 2026
9f02ad6
feat(ibmcloud): auto-discover Power VS system type based on pool capa…
deekay2310 Jun 17, 2026
246c1c0
fix(ibmcloud): harden GH runner params and system pool selection
deekay2310 Jun 17, 2026
28e55f7
fix(ibmcloud): remove nonexistent /opt/dotnet from s390x snippet
deekay2310 Jun 22, 2026
107ab7f
feat(ibmcloud): zone-level system type discovery with retry on capacity
deekay2310 Jun 22, 2026
43b2865
fix(ibmcloud): harden Power VS provisioning and address PR review
deekay2310 Jun 23, 2026
93c91ee
fix(ibmcloud): default pi-sys-type to auto-discovery and retry on tim…
deekay2310 Jun 24, 2026
7c2ee65
ci: retrigger after transient quay.io pull failure
deekay2310 Jun 24, 2026
a1d6416
Merge branch 'main' into ibmcloud-gh-runners
deekay2310 Jun 25, 2026
baad7fb
refactor(github): pass arch to GithubRunnerArgs, internalize runner i…
deekay2310 Jun 30, 2026
d75b79c
refactor(ibmcloud): remove PowerVS capacity retry logic and default t…
deekay2310 Jun 30, 2026
9761706
fix(github): point runner image repo to aipcc-cicd org
deekay2310 Jun 30, 2026
a8e6f9c
refactor(github): pin runner image to aipcc-cicd v2.0.0 tag and pass …
deekay2310 Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/smoke-test-s390x.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: s390x Runner Smoke Test
on: workflow_dispatch
jobs:
smoke-test:
runs-on: [self-hosted, S390X]
steps:
- name: Check architecture
run: |
echo "Architecture: $(uname -m)"
cat /etc/os-release | grep PRETTY_NAME
echo "Runner is alive on $(arch)"
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/aws/hosts/fedora.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func getFedoraCreate() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/aws/hosts/mac.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func getMacRequest() *cobra.Command {
ResultsOutput: viper.GetString(params.ConnectionDetailsOutput),
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/aws/hosts/rhel.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func getRHELCreate() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/aws/hosts/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func getWindowsCreate() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/aws/services/mac-pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func request() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/azure/hosts/rhel.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func getCreateRHEL() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/mapt/cmd/azure/hosts/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func getCreateWindowsDesktop() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(params.LinuxGithubArch()),
GLRunnerArgs: params.GitLabRunnerArgs(params.LinuxGitLabArch()),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
3 changes: 2 additions & 1 deletion cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package hosts

import (
"github.com/redhat-developer/mapt/cmd/mapt/cmd/params"
"github.com/redhat-developer/mapt/pkg/integrations/github"
"github.com/redhat-developer/mapt/pkg/integrations/gitlab"
maptContext "github.com/redhat-developer/mapt/pkg/manager/context"
ibmpower "github.com/redhat-developer/mapt/pkg/provider/ibmcloud/action/ibm-power"
Expand Down Expand Up @@ -52,7 +53,7 @@ func ibmPowerCreate() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(&github.Ppc64le),
GLRunnerArgs: params.GitLabRunnerArgs(&gitlab.Ppc64le),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
3 changes: 2 additions & 1 deletion cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package hosts

import (
"github.com/redhat-developer/mapt/cmd/mapt/cmd/params"
"github.com/redhat-developer/mapt/pkg/integrations/github"
"github.com/redhat-developer/mapt/pkg/integrations/gitlab"
maptContext "github.com/redhat-developer/mapt/pkg/manager/context"
ibmz "github.com/redhat-developer/mapt/pkg/provider/ibmcloud/action/ibm-z"
Expand Down Expand Up @@ -52,7 +53,7 @@ func ibmZCreate() *cobra.Command {
Debug: viper.IsSet(params.Debug),
DebugLevel: viper.GetUint(params.DebugLevel),
CirrusPWArgs: params.CirrusPersistentWorkerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(),
GHRunnerArgs: params.GithubRunnerArgs(&github.S390x),
GLRunnerArgs: params.GitLabRunnerArgs(&gitlab.S390x),
Tags: viper.GetStringMapString(params.Tags),
},
Expand Down
54 changes: 42 additions & 12 deletions cmd/mapt/cmd/params/params.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package params

import (
"os"

"github.com/redhat-developer/mapt/pkg/integrations/cirrus"
"github.com/redhat-developer/mapt/pkg/integrations/github"
"github.com/redhat-developer/mapt/pkg/integrations/gitlab"
Expand Down Expand Up @@ -155,8 +157,8 @@ const (
PIProcTypeDesc string = "PowerVS processor type (shared, dedicated, capped)"
PIProcTypeDefault string = "shared"
PISysType string = "pi-sys-type"
PISysTypeDesc string = "PowerVS system type (s922, s1022, e880, e980)"
PISysTypeDefault string = "s1022"
PISysTypeDesc string = "PowerVS system type (e.g. e1080, s1022, s1122)"
PISysTypeDefault string = "e1080"
PIStorageType string = "pi-storage-type"
PIStorageTypeDesc string = "PowerVS storage tier for instance and data volume (tier1, tier3)"
PIStorageTypeDefault string = "tier1"
Expand Down Expand Up @@ -282,18 +284,38 @@ func AddGHActionsFlags(fs *pflag.FlagSet) {
fs.StringSlice(ghActionsRunnerLabels, nil, GHActionsRunnerLabelsDesc)
}

func GithubRunnerArgs() *github.GithubRunnerArgs {
if viper.IsSet(ghActionsRunnerToken) {
return &github.GithubRunnerArgs{
Token: viper.GetString(ghActionsRunnerToken),
RepoURL: viper.GetString(ghActionsRunnerRepo),
Labels: viper.GetStringSlice(ghActionsRunnerLabels),
Platform: &github.Linux,
Arch: linuxArchAsGithubActionsArch(
viper.GetString(LinuxArch)),
func GithubRunnerArgs(arch *github.Arch) *github.GithubRunnerArgs {
token := viper.GetString(ghActionsRunnerToken)
repoURL := viper.GetString(ghActionsRunnerRepo)
pat := os.Getenv("GITHUB_TOKEN")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user doesn't provide --ghactions-runner-token, the code uses GITHUB_TOKEN from their environment to auto-generate a runner registration token via the GitHub API (GenerateRegistrationToken). This way users don't have to manually create a short-lived registration token before each run.


if token == "" && pat == "" {
return nil
}

if repoURL == "" {
logging.Error("--ghactions-runner-repo is required for GitHub Actions runner setup")
return nil
}

if token == "" {
logging.Info("no --ghactions-runner-token provided, auto-generating from GITHUB_TOKEN")
var err error
token, err = github.GenerateRegistrationToken(pat, repoURL)
if err != nil {
logging.Errorf("failed to auto-generate runner registration token: %v", err)
return nil
}
logging.Info("runner registration token generated successfully")
}

return &github.GithubRunnerArgs{
Token: token,
RepoURL: repoURL,
Labels: viper.GetStringSlice(ghActionsRunnerLabels),
Platform: &github.Linux,
Arch: arch,
}
return nil
}

func AddCirrusFlags(fs *pflag.FlagSet) {
Expand Down Expand Up @@ -357,10 +379,18 @@ func linuxArchAsCirrusArch(arch string) *cirrus.Arch {
return &cirrus.Arm64
}

func LinuxGithubArch() *github.Arch {
return linuxArchAsGithubActionsArch(viper.GetString(LinuxArch))
}

func linuxArchAsGithubActionsArch(arch string) *github.Arch {
switch arch {
case "x86_64":
return &github.Amd64
case "ppc64le":
return &github.Ppc64le
case "s390x":
return &github.S390x
}
return &github.Arm64
}
Expand Down
68 changes: 68 additions & 0 deletions pkg/integrations/github/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package github

import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)

type registrationTokenResponse struct {
Token string `json:"token"`
ExpiresAt string `json:"expires_at"`
}

// GenerateRegistrationToken calls the GitHub API to create a short-lived
// runner registration token for the given repository.
// pat is a Personal Access Token with repo admin scope.
// repoURL is in the form "owner/repo" or "https://github.com/owner/repo".
func GenerateRegistrationToken(pat, repoURL string) (string, error) {
ownerRepo := repoURL
ownerRepo = strings.TrimPrefix(ownerRepo, "https://github.com/")
ownerRepo = strings.TrimPrefix(ownerRepo, "http://github.com/")
ownerRepo = strings.TrimSuffix(ownerRepo, "/")

parts := strings.Split(ownerRepo, "/")
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return "", fmt.Errorf("invalid repo format %q, expected owner/repo", repoURL)
}

url := fmt.Sprintf("https://api.github.com/repos/%s/%s/actions/runners/registration-token", parts[0], parts[1])
Comment thread
deekay2310 marked this conversation as resolved.

req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
return "", fmt.Errorf("creating request: %w", err)
}
req.Header.Set("Authorization", "token "+pat)
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")

client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("calling GitHub API: %w", err)
}
defer func() { _ = resp.Body.Close() }()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("reading response: %w", err)
}

if resp.StatusCode != http.StatusCreated {
return "", fmt.Errorf("GitHub API returned %d: %s (ensure GITHUB_TOKEN has admin scope on the repo)", resp.StatusCode, string(body))
}

var tokenResp registrationTokenResponse
if err := json.Unmarshal(body, &tokenResp); err != nil {
return "", fmt.Errorf("parsing response: %w", err)
}

if tokenResp.Token == "" {
return "", fmt.Errorf("empty token in GitHub API response")
}

return tokenResp.Token, nil
}
34 changes: 27 additions & 7 deletions pkg/integrations/github/ghrunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ var runnerVersion = "2.317.0"
// 1 is version, 2 is platform: (win, linux, osx), 3 is arch: (arm64, x64, arm)
const runnerBaseURL = "https://github.com/actions/runner/releases/download/v%[1]s/actions-runner-%[2]s-%[3]s-%[1]s"

const runnerImageRepo = "https://github.com/aipcc-cicd/action-runner-image-pz.git"
const runnerImageRepoVersion = "v2.0.0"

//go:embed snippet-darwin.sh
var snippetDarwin []byte

Expand All @@ -23,12 +26,23 @@ var snippetLinux []byte
//go:embed snippet-windows.ps1
var snippetWindows []byte

//go:embed snippet-linux-ppc64le.sh
var snippetLinuxPpc64le []byte

//go:embed snippet-linux-s390x.sh
var snippetLinuxS390x []byte

var snippets map[Platform][]byte = map[Platform][]byte{
Darwin: snippetDarwin,
Linux: snippetLinux,
Windows: snippetWindows,
}

var archSnippets map[Arch][]byte = map[Arch][]byte{
Ppc64le: snippetLinuxPpc64le,
S390x: snippetLinuxS390x,
}

var runnerArgs *GithubRunnerArgs

func Init(args *GithubRunnerArgs) {
Expand All @@ -40,17 +54,23 @@ func (args *GithubRunnerArgs) GetUserDataValues() *integrations.UserDataValues {
return nil
}
return &integrations.UserDataValues{
Name: args.Name,
Token: args.Token,
Labels: getLabels(),
RepoURL: args.RepoURL,
CliURL: downloadURL(),
Name: args.Name,
Token: args.Token,
Labels: getLabels(),
RepoURL: args.RepoURL,
CliURL: downloadURL(),
RunnerImageRepo: runnerImageRepo,
RunnerImageRepoVersion: runnerImageRepoVersion,
}
}

func (args *GithubRunnerArgs) GetSetupScriptTemplate() string {
templateConfig := string(snippets[*runnerArgs.Platform][:])
return templateConfig
if *runnerArgs.Platform == Linux && runnerArgs.Arch != nil {
if archSnippet, ok := archSnippets[*runnerArgs.Arch]; ok {
return string(archSnippet[:])
}
}
return string(snippets[*runnerArgs.Platform][:])
}

func GetRunnerArgs() *GithubRunnerArgs {
Expand Down
44 changes: 44 additions & 0 deletions pkg/integrations/github/snippet-linux-ppc64le.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -euo pipefail

dnf install -y git-core

git clone --branch "{{ .RunnerImageRepoVersion }}" --depth=1 "{{ .RunnerImageRepo }}" /opt/action-runner-image-pz

cd /opt/action-runner-image-pz
bash -c '. scripts/vm.sh rhel 9 minimal --skip-snap-lxd' || true

# The upstream configure-system.sh runs chmod -R 777 /usr/share which breaks
# sshd privilege separation (/usr/share/empty.sshd must be root-owned, not
# world-writable). Also fix PAM duplicates from configure-limits.sh.
chmod 755 /usr/share/empty.sshd 2>/dev/null || true
chown root:root /usr/share/empty.sshd 2>/dev/null || true
for f in /etc/pam.d/system-auth /etc/pam.d/password-auth; do
if [ -f "$f" ]; then
awk '!seen[$0]++' "$f" > "${f}.tmp" && mv "${f}.tmp" "$f"
fi
done
systemctl restart sshd 2>/dev/null || true

if [ ! -f /opt/runner-cache/config.sh ]; then
echo "Runner binary not found after build" >&2
exit 1
fi

id -u runner &>/dev/null || useradd -m -s /bin/bash runner
chown -R runner:runner /opt/runner-cache

sudo -u runner bash -c '
cd /opt/runner-cache

./config.sh \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we explore options in config to only install the cli and set it up to join as runner?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the fork is moved into aipcc-cicd with tagged releases, we can publish pre-built binaries and simplify this to a download + configure like the x86_64/arm64 snippet.

--unattended \
--disableupdate \
--ephemeral \
--name "{{ .Name }}" \
--labels "{{ .Labels }}" \
--url "{{ .RepoURL }}" \
--token "{{ .Token }}"

nohup ./run.sh > /tmp/gh-runner.log 2>&1 &
'
Loading