Skip to content
This repository has been archived by the owner on Oct 21, 2024. It is now read-only.

Add Go and E2E tests and the CI #2

Merged
merged 6 commits into from
Mar 15, 2024
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
51 changes: 51 additions & 0 deletions .github/workflows/ci-go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# https://github.com/golang/go/issues/59968
name: ʕ◔ϖ◔ʔ

on:
push:
branches:
- main
paths:
- '.github/workflows/ci-go.yml'
- '**.go'
- 'go.*'
pull_request:
paths:
- '.github/workflows/ci-go.yml'
- '**.go'
- 'go.*'
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache-dependency-path: 'go.sum'
- run: go build -v -race ./...
test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache-dependency-path: 'go.sum'
- run: go test ./...
lint:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache-dependency-path: 'go.sum'
- run: go vet ./...
- name: check format
run: go fmt ./... && git add --intent-to-add . && git diff --exit-code
34 changes: 34 additions & 0 deletions .github/workflows/ci-googleapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: 📡

on:
push:
branches:
- main
paths:
- '.github/workflows/ci-go.yml'
- '**.go'
- 'go.*'
pull_request:
types:
- labeled
schedule:
# Every 10:42 JST
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
- cron: '42 1 * * *'
workflow_dispatch:

jobs:
test-actual-api:
# This test actually send request to Google API, so prevent frequently triggers with the labeled or not
if: >-
(github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'apitest')) ||
(github.event_name != 'pull_request')
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
cache-dependency-path: 'go.sum'
- run: go test -tags=apitest ./...
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@
"command": ["nixpkgs-fmt"]
}
}
},
"gopls": {
"build.buildFlags": ["-tags=apitest"]
}
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# gwurl - "Google distributing Windows installer"'s URL

[![CI - Go Status](https://github.com/kachick/gwurl/actions/workflows/ci-go.yml/badge.svg?branch=main)](https://github.com/kachick/gwurl/actions/workflows/ci-go.yml?query=branch%3Amain+)
[![CI - E2E Status](https://github.com/kachick/gwurl/actions/workflows/ci-googleapi.yml/badge.svg?branch=main)](https://github.com/kachick/gwurl/actions/workflows/ci-googleapi.yml?query=branch%3Amain+)
[![CI - Nix Status](https://github.com/kachick/gwurl/actions/workflows/ci-nix.yml/badge.svg?branch=main)](https://github.com/kachick/gwurl/actions/workflows/ci-nix.yml?query=branch%3Amain+)

## Usage
Expand Down
5 changes: 4 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ tasks:
- go build ./...
test:
cmds:
- echo 'Update here'
- go test ./...
test-all:
cmds:
- go test -tags=apitest ./...
fmt:
cmds:
- dprint fmt
Expand Down
160 changes: 14 additions & 146 deletions cmd/gwurl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,13 @@ package main
import (
"flag"
"fmt"
"io"
"log"
"net/url"
"os"
"path"
"slices"
"strings"

"bytes"
"encoding/xml"
"net/http"

"golang.org/x/xerrors"
"github.com/kachick/gwurl/internal/googleapi"
"github.com/kachick/gwurl/internal/taggedurl"
)

var (
Expand All @@ -26,135 +20,6 @@ var (
revision = "rev"
)

type TaggedURL struct {
appguid string
ap string
appname string
needsadmin bool
filename string
}

type Action struct {
Event string `xml:"event,attr"`
Run string `xml:"run,attr"`
}

// NOTE: Use `InnerXML string `xml:",innerxml"“ to inspect unknown fields: https://stackoverflow.com/a/38509722
type Response struct {
XMLName xml.Name `xml:"response"`
App struct {
Status string `xml:"status"`
UpdateCheck struct {
Manifest struct {
Version string `xml:"version,attr"`
Actions []Action `xml:"actions>action"`
} `xml:"manifest"`

Urls []struct {
Codebase string `xml:"codebase,attr"`
} `xml:"urls>url"`
} `xml:"updatecheck"`
} `xml:"app"`
}

type GoogleApiOs struct {
platform string
version string
architecture string
}

type GoogleApiApp struct {
appid string
ap string
}

func BuildGoogleApiPostXml(apiOs GoogleApiOs, apiApp GoogleApiApp) string {
return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<request protocol="3.0">
<os platform="%s" version="%s" arch="%s" />
<app appid="%s" version="" ap="%s">
<updatecheck />
</app>
</request>
`, apiOs.platform, apiOs.version, apiOs.architecture, apiApp.appid, apiApp.ap)
}

func PostGoogleAPI(apiOs GoogleApiOs, apiApp GoogleApiApp) (Response, error) {
body := []byte(BuildGoogleApiPostXml(apiOs, apiApp))

req, err := http.NewRequest("POST", "https://update.googleapis.com/service/update2", bytes.NewBuffer(body))
if err != nil {
return Response{}, xerrors.Errorf("Error creating request: %w", err)
}

req.Header.Set("Content-Type", "application/xml; charset=utf-8")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return Response{}, xerrors.Errorf("Error posting request: %w", err)
}
defer resp.Body.Close()

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

var responseObject Response
err = xml.Unmarshal(respBody, &responseObject)
if err != nil {
return Response{}, xerrors.Errorf("Error unmarshalling response: %w", err)
}

return responseObject, nil
}

func ParseTaggedURL(likeTaggedUrl string) TaggedURL {
// [scheme:][//[userinfo@]host][/]path[?query][#fragment]
taggedUrl, err := url.ParseRequestURI(likeTaggedUrl)
if err != nil {
log.Fatalf("Cannot parse given URL: %+v", err)
}
if !strings.HasSuffix(taggedUrl.Host, "google.com") {
log.Fatalf("Given URL looks not a goole: %s", taggedUrl.Host)
}

prefixWithQuery, filename := path.Split(taggedUrl.Path)
// Intentioanlly avoiding path.Split for the getting nth element. Not the prefix and last
dirs := strings.Split(prefixWithQuery, "/")
qsi := slices.IndexFunc(dirs, func(dir string) bool { return strings.Contains(dir, "appguid") })
qs := dirs[qsi]
query, err := url.ParseQuery(qs)
if err != nil {
log.Fatalf("Cannot parse given query: %+v", err)
}
appguid, ok := query["appguid"]
if !ok {
log.Fatalf("No appguid: %s", appguid)
}
ap, ok := query["ap"]
if !ok {
log.Fatalf("No ap: %s", ap)
}
appname, ok := query["appname"]
if !ok {
log.Fatalf("No appname: %s", ap)
}
needsadmin, ok := query["needsadmin"]
if !ok {
log.Fatalf("No needsadmin: %s", needsadmin)
}

return TaggedURL{
appguid: appguid[0],
ap: ap[0],
appname: appname[0],
needsadmin: needsadmin[0] == "true",
filename: filename,
}
}

func main() {
versionFlag := flag.Bool("version", false, "print the version of this program")

Expand Down Expand Up @@ -188,22 +53,25 @@ $ gwurl --version
}

taggedUrl := os.Args[1]
parsed := ParseTaggedURL(taggedUrl)
parsed, err := taggedurl.ParseTaggedURL(taggedUrl)
if err != nil {
log.Fatalf("Cannot parse given URL: %+v", err)
}
fmt.Printf("%+v\n", parsed)

resp, err := PostGoogleAPI(GoogleApiOs{
platform: "win",
version: "10",
architecture: "x64",
}, GoogleApiApp{
appid: parsed.appguid,
ap: parsed.ap,
resp, err := googleapi.PostGoogleAPI(googleapi.GoogleApiOs{
Platform: "win",
Version: "10",
Architecture: "x64",
}, googleapi.GoogleApiApp{
Appid: parsed.Appguid,
Ap: parsed.Ap,
})
if err != nil {
log.Fatalf("Cannot ask to Google API: %+v", err)
}

installerActionIdx := slices.IndexFunc(resp.App.UpdateCheck.Manifest.Actions, func(a Action) bool {
installerActionIdx := slices.IndexFunc(resp.App.UpdateCheck.Manifest.Actions, func(a googleapi.Action) bool {
return a.Event == "install"
})
installerFilename := resp.App.UpdateCheck.Manifest.Actions[installerActionIdx].Run
Expand Down
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
# When updating go.mod or go.sum, update this sha together as following
# vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
# (`pkgs.lib.fakeSha256` returns invalid string in thesedays... :<)
vendorHash = "sha256-sODHIjL/iaWzH0iarh8Y9N7hZGKznbUxwE5xOPwEFvc=";
vendorHash = "sha256-hC1eg2mC3Qp0QGFj3pTMIOyjrMV9Yx+hqvupxUG17OQ=";
};

packages.default = packages.gwurl;
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ module github.com/kachick/gwurl
go 1.22.1

require golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028

require github.com/google/go-cmp v0.6.0 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
Loading