Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
93 changes: 49 additions & 44 deletions .github/workflows/wrapper-manager-image.yml
Original file line number Diff line number Diff line change
@@ -1,52 +1,57 @@
name: wrapper-manager-image-build
name: Build amd64 Docker image

on:
workflow_dispatch:
push:
branches: [ "main" ]
branches: [main]
pull_request:
branches: [ "main" ]
workflow_dispatch:
branches: [main]

jobs:
permissions:
contents: read
packages: write

concurrency:
group: wrapper-manager-amd64-${{ github.ref }}
cancel-in-progress: true

build:
env:
IMAGE_NAME: ghcr.io/amdl-web/wrapper-manager

jobs:
docker:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23'

- name: Build
run: go build .

- name: Download wrapper-manager basic image
run: wget https://github.com/WorldObservationLog/wrapper-manager/releases/download/wrapper-manager-image/wrapper-manager.qcow2

- name: Mount image
run: |
sudo apt-get update
sudo apt-get install -y qemu-utils
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 wrapper-manager.qcow2
sudo mkdir /mnt/wrapper-manager
sudo mount /dev/nbd0p3 /mnt/wrapper-manager/

- name: Prepare wrapper-manager
run: |
sudo mkdir /mnt/wrapper-manager/root/wrapper-manager
sudo mv wrapper-manager /mnt/wrapper-manager/root/wrapper-manager
sudo bash -c 'cd /mnt/wrapper-manager/root/wrapper-manager && ./wrapper-manager -prepare'

- name: Unmount image
run: |
sudo umount /mnt/wrapper-manager/
sudo qemu-nbd --disconnect /dev/nbd0

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: wrapper-manager-image
path: wrapper-manager.qcow2
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate image metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha

- name: Build and publish amd64 image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
29 changes: 19 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
FROM golang:1.23 as builder
FROM --platform=$BUILDPLATFORM golang:1.23 AS builder

ARG TARGETOS
ARG TARGETARCH

WORKDIR /app

COPY go.mod go.sum ./
COPY proto/go.mod proto/go.sum ./proto/
RUN go mod download

COPY . .
# RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct
RUN go mod tidy
RUN GOOS=linux go build -o wrapper-manager
RUN test "$TARGETARCH" = "amd64" && \
CGO_ENABLED=0 GOOS="$TARGETOS" GOARCH="$TARGETARCH" \
go build -trimpath -ldflags="-s -w" -o /out/wrapper-manager .

FROM ubuntu:24.04

FROM ubuntu:latest
WORKDIR /root

WORKDIR /root/
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/wrapper-manager .
RUN apt-get update && apt-get install -y ca-certificates
RUN chmod +x ./wrapper-manager
COPY --from=builder /out/wrapper-manager ./wrapper-manager

ENTRYPOINT ["./wrapper-manager"]
EXPOSE 8080
EXPOSE 8080
5 changes: 2 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,6 @@ func main() {
flag.StringVar(&PROXY, "proxy", "", "proxy for wrapper and manager")
flag.StringVar(&DeviceInfo, "device-info", "Music/5.0.2/Android/10/Pixel 10/7663314/en-US/en-US/dc28071e371c439e", "device info for wrapper")
flag.Parse()
_ = mirror

log.SetOutput(os.Stdout)
if *debug {
Expand All @@ -514,8 +513,8 @@ func main() {
log.Panicln("root permission required")
}

if _, err := os.Stat("data/wrapper/wrapper"); errors.Is(err, os.ErrNotExist) {
log.Panicln("wrapper binary not found in data/wrapper/wrapper. Please build and place it there first.")
if err := PrepareWrapper(*mirror); err != nil {
log.Panicf("prepare x86_64 wrapper: %v", err)
}

if _, err := os.Stat("data/storefront_ids.json"); errors.Is(err, os.ErrNotExist) {
Expand Down
98 changes: 85 additions & 13 deletions wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import (
"strings"
)

const (
wrapperReleaseTag = "wrapper.x86_64.latest"
wrapperAssetName = "Wrapper.x86_64.latest.zip"
)

var wrapperReleaseAPIURL = "https://api.github.com/repos/AMDL-Web/wrapper/releases/tags/" + wrapperReleaseTag

func parseStorefrontID(id string) string {
sfID, err := strconv.Atoi(strings.Split(id, "-")[0])
if err != nil {
Expand Down Expand Up @@ -43,26 +50,31 @@ func parseStorefrontID(id string) string {
return ""
}

func PrepareWrapper(mirror bool) {
var wrapperZipPath string
if runtime.GOARCH == "amd64" {
wrapperZipPath = "data/wrapper-x86_64.zip"
} else if runtime.GOARCH == "arm64" {
wrapperZipPath = "data/wrapper-arm64.zip"
func PrepareWrapper(mirror bool) error {
return prepareWrapper(mirror, runtime.GOARCH)
}

func prepareWrapper(mirror bool, arch string) error {
if arch != "amd64" {
return fmt.Errorf("wrapper auto-install only supports x86_64, current architecture is %s", arch)
}
wrapperZipPath := "data/wrapper-x86_64.zip"
if _, err := os.Stat("data/wrapper/wrapper"); os.IsNotExist(err) {
if _, err := os.Stat(wrapperZipPath); os.IsNotExist(err) {
DownloadWrapperRelease(mirror)
if err := DownloadWrapperRelease(mirror); err != nil {
return err
}
}
err = unzip.New(wrapperZipPath, "data/wrapper").Extract()
err := unzip.New(wrapperZipPath, "data/wrapper").Extract()
if err != nil {
panic(err)
return fmt.Errorf("extract wrapper release: %w", err)
}
err = os.Chmod("data/wrapper/wrapper", 0777)
if err != nil {
panic(err)
return fmt.Errorf("make wrapper executable: %w", err)
}
}
return nil
}

func WrapperInitial(account string, password string) {
Expand Down Expand Up @@ -234,7 +246,7 @@ func KillWrapper(id string) error {
}

func provide2FACode(id string, code string) {
path := "data/wrapper/rootfs/data/instances/"+id+"/2fa.txt"
path := "data/wrapper/rootfs/data/instances/" + id + "/2fa.txt"
err := os.WriteFile(path, []byte(code), 0777)
if err != nil {
log.Warnf("failed to write 2fa.txt: %v", err)
Expand All @@ -248,8 +260,68 @@ func RemoveWrapperData(id string) {
}
}

func DownloadWrapperRelease(mirror bool) {
panic("Downloading wrapper binary is disabled. Please provide the binary manually in data/wrapper/wrapper.")
func DownloadWrapperRelease(mirror bool) error {
resp, err := GetHttpClient().Get(wrapperReleaseAPIURL)
if err != nil {
return fmt.Errorf("request wrapper release: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("request wrapper release: %s", resp.Status)
}

var release struct {
Assets []struct {
Name string `json:"name"`
BrowserDownloadURL string `json:"browser_download_url"`
} `json:"assets"`
}
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
return fmt.Errorf("decode wrapper release: %w", err)
}

var downloadURL string
for _, asset := range release.Assets {
if asset.Name == wrapperAssetName {
downloadURL = asset.BrowserDownloadURL
break
}
}
if downloadURL == "" {
return fmt.Errorf("wrapper release %s has no %s asset", wrapperReleaseTag, wrapperAssetName)
}
if mirror {
downloadURL = strings.Replace(downloadURL, "github.com", "gh-proxy.com/github.com", 1)
}

wrapperResp, err := GetHttpClient().Get(downloadURL)
if err != nil {
return fmt.Errorf("download wrapper release: %w", err)
}
defer wrapperResp.Body.Close()
if wrapperResp.StatusCode < 200 || wrapperResp.StatusCode >= 300 {
return fmt.Errorf("download wrapper release: %s", wrapperResp.Status)
}
if err := os.MkdirAll("data", 0o755); err != nil {
return fmt.Errorf("create data directory: %w", err)
}
temp, err := os.CreateTemp("data", "wrapper-x86_64-*.zip")
if err != nil {
return fmt.Errorf("create wrapper download: %w", err)
}
tempPath := temp.Name()
defer os.Remove(tempPath)
if _, err := io.Copy(temp, wrapperResp.Body); err != nil {
temp.Close()
return fmt.Errorf("save wrapper release: %w", err)
}
if err := temp.Close(); err != nil {
return fmt.Errorf("close wrapper release: %w", err)
}
if err := os.Rename(tempPath, "data/wrapper-x86_64.zip"); err != nil {
return fmt.Errorf("finalize wrapper release: %w", err)
}
return nil
}

func DownloadStorefrontIds() {
Expand Down
Loading
Loading