11 Commits

Author SHA1 Message Date
appleboy
b498341857 build: improve compression and update GitHub actions (#168)
- Add `dist` to .gitignore for gorelease binary folder
- Replace tar command with xz command in .goreleaser.yaml for better compression

ref: https://gitea.com/gitea/homebrew-gitea/pulls/164

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/168
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-05-01 21:59:43 +08:00
appleboy
0d727eb262 build: optimize Dockerfile and update dependencies (#162)
- Update base images to golang:1.20-alpine3.17 and alpine:3.17
- Replace `--update-cache` with `--no-cache` in apk add command
- Specify exact versions for make, git, and bash packages

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/162
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-04-29 12:07:15 +08:00
vbrandl
7c71c94366 Document persisting /data for docker container (#160)
`/data` must be kept between container restarts.

Co-authored-by: Valentin Brandl <mail@vbrandl.net>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/160
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: vbrandl <vbrandl@noreply.gitea.io>
Co-committed-by: vbrandl <vbrandl@noreply.gitea.io>
2023-04-29 03:05:00 +08:00
appleboy
49d2cb0cb5 ci: improve API usage and test robustness across platforms (#159)
- Add `dir: ./dist/` to `.goreleaser.yaml` builds configuration

fix https://gitea.com/gitea/act_runner/issues/158

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/159
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-04-28 23:46:46 +08:00
sillyguodong
85626b6bbd Support configuration variables (#157)
related to: https://gitea.com/gitea/act_runner/issues/127

`act_runner` only needs to pass `vars` from `Gitea` to `act`.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/157
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-04-28 22:06:08 +08:00
Zettat123
35400f76fa Add parent directory for working directory (#154)
Fixes #145

At present, the working directory of a work flow is a path like `/<owner>/<repo>`, so the directory may conflict with system directory like `/usr/bin`. We need to add a parent directory for the working directory.
In this PR, the parent directory is `/workspace` by default and users could configure it by the `workdir_parent` option.

This change doesn't affect the host mode because in host mode the working directory will always be in `$HOME/.cache/act/` directory.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/154
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-04-28 22:03:52 +08:00
Jason Song
0cf31b2d22 Update docker image tag (#153)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/153
2023-04-27 15:02:39 +08:00
Søren L. Hansen
c8cc7b2448 Workflow commands (#149)
Establishes a simple framework for supporting workflow commands.

Fully implements `::add-mask::`, `::debug::`, and `::stop-commands::`.

Addresses #148

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/149
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Søren L. Hansen <sorenisanerd@gmail.com>
Co-committed-by: Søren L. Hansen <sorenisanerd@gmail.com>
2023-04-27 12:32:48 +08:00
Jason Song
3be962cdb3 Rename the download folder from main -> nightly (#152)
Close https://gitea.com/gitea/act_runner/issues/117

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/152
2023-04-27 12:23:15 +08:00
Jason Song
a5edbc9ac4 Release docker images (#151)
Did some tests to make sure it worked.

See https://hub.docker.com/r/gitea/act_runner/tags

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/151
2023-04-27 12:08:41 +08:00
seepine
66bab3d805 ci(actions): add build docker image workflow (#118)
### Add secret
```
DOCKERHUB_TOKEN=xxx
```

### Tag
when tag like `v1.0.0`, it will build multi platform docker image `gitea/act_runner:1.0.0` and `gitea/act_runner:latest`, then push to docker hub

### Use
> volume `/data` save `.runner` config file
> volume `/root/.cache` save actcache and actions cache

```sh
docker run -e GITEA_INSTANCE_URL=***                    \
           -e GITEA_RUNNER_REGISTRATION_TOKEN=***       \
           -e GITEA_RUNNER_NAME=***                     \
           -v /var/run/docker.sock:/var/run/docker.sock \
           -v /root/act_runner/data:/data               \
           -v /root/act_runner/cache:/root/.cache       \
           gitea/act_runner
```
Test join runners success
![image](/attachments/f5287e93-e27c-420f-a3d5-8f9b54bfdbb6)
Test run action success
![image](/attachments/7235af17-f598-4fc8-88b4-d4771b1f07cd)

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/118
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: seepine <seepine@noreply.gitea.io>
Co-committed-by: seepine <seepine@noreply.gitea.io>
2023-04-27 11:32:28 +08:00
12 changed files with 390 additions and 27 deletions

View File

@@ -1,4 +1,4 @@
name: goreleaser
name: release-nightly
on:
push:
@@ -45,3 +45,53 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
release-image:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
env:
DOCKER_ORG: gitea
DOCKER_LATEST: nightly
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0 # all history for all branches and tags
- name: dockerfile lint check
uses: https://github.com/hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get Meta
id: meta
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v4
env:
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
with:
context: .
file: ./Dockerfile
platforms: |
linux/amd64
linux/arm64
push: true
tags: |
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}

View File

@@ -1,6 +1,6 @@
name: goreleaser
name: release-tag
on:
on:
push:
tags:
- '*'
@@ -56,3 +56,53 @@ jobs:
GORELEASER_FORCE_TOKEN: 'gitea'
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
release-image:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
env:
DOCKER_ORG: gitea
DOCKER_LATEST: latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0 # all history for all branches and tags
- name: dockerfile lint check
uses: https://github.com/hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Get Meta
id: meta
run: |
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
echo REPO_VERSION=$(git describe --tags --always | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v4
env:
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
with:
context: .
file: ./Dockerfile
platforms: |
linux/amd64
linux/arm64
push: true
tags: |
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ steps.meta.outputs.REPO_VERSION }}
${{ env.DOCKER_ORG }}/${{ steps.meta.outputs.REPO_NAME }}:${{ env.DOCKER_LATEST }}

View File

@@ -1,5 +1,5 @@
name: checks
on:
on:
- push
- pull_request
@@ -36,3 +36,7 @@ jobs:
run: make build
- name: test
run: make test
- name: dockerfile lint check
uses: https://github.com/hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile

2
.gitignore vendored
View File

@@ -8,3 +8,5 @@ coverage.txt
# MS VSCode
.vscode
__debug_bin
# gorelease binary folder
dist

View File

@@ -71,9 +71,8 @@ builds:
no_unique_dist_dir: true
hooks:
post:
- cmd: tar -cJf {{ .Path }}.xz {{ .Path }}
env:
- XZ_OPT=-9
- cmd: xz -k -9 {{ .Path }}
dir: ./dist/
- cmd: sh .goreleaser.checksum.sh {{ .Path }}
- cmd: sh .goreleaser.checksum.sh {{ .Path }}.xz
@@ -101,11 +100,11 @@ snapshot:
name_template: "{{ .Branch }}-devel"
nightly:
name_template: "{{ .Branch }}"
name_template: "nightly"
gitea_urls:
api: https://gitea.com/api/v1
download: https://gitea.com
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

View File

@@ -1,15 +1,15 @@
FROM golang:alpine as builder
RUN apk add --update-cache make git
FROM golang:1.20-alpine3.17 as builder
RUN apk add --no-cache make=4.3-r1 git=2.38.5-r0
COPY . /opt/src/act_runner
WORKDIR /opt/src/act_runner
RUN make clean && make build
FROM alpine as runner
RUN apk add --update-cache \
git bash \
&& rm -rf /var/cache/apk/*
FROM alpine:3.17
RUN apk add --no-cache \
git=2.38.5-r0 bash=5.2.15-r0 \
&& rm -rf /var/cache/apk/*
COPY --from=builder /opt/src/act_runner/act_runner /usr/local/bin/act_runner
COPY run.sh /opt/act/run.sh

View File

@@ -91,5 +91,29 @@ You can specify the configuration file path with `-c`/`--config` argument.
### Run a docker container
```sh
docker run -e GITEA_INSTANCE_URL=http://192.168.8.18:3000 -e GITEA_RUNNER_REGISTRATION_TOKEN=<runner_token> -v /var/run/docker.sock:/var/run/docker.sock --name my_runner gitea/act_runner:nightly
docker run -e GITEA_INSTANCE_URL=http://192.168.8.18:3000 -e GITEA_RUNNER_REGISTRATION_TOKEN=<runner_token> -v /var/run/docker.sock:/var/run/docker.sock -v $PWD/data:/data --name my_runner gitea/act_runner:nightly
```
The `/data` directory inside the docker container contains the runner API keys after registration.
It must be persisted, otherwise the runner would try to register again, using the same, now defunct registration token.
### Running in docker-compose
```yml
...
gitea:
image: gitea/gitea
...
runner:
image: gitea/act_runner
restart: always
depends_on:
- gitea
volumes:
- ./data/act_runner:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
- GITEA_INSTANCE_URL=<instance url>
- GITEA_RUNNER_REGISTRATION_TOKEN=<registration token>
```

View File

@@ -166,9 +166,9 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
}
runnerConfig := &runner.Config{
// On Linux, Workdir will be like "/<owner>/<repo>"
// On Windows, Workdir will be like "\<owner>\<repo>"
Workdir: filepath.FromSlash(string(filepath.Separator) + preset.Repository),
// On Linux, Workdir will be like "/<parent_directory>/<owner>/<repo>"
// On Windows, Workdir will be like "\<parent_directory>\<owner>\<repo>"
Workdir: filepath.FromSlash(fmt.Sprintf("/%s/%s", r.cfg.Container.WorkdirParent, preset.Repository)),
BindWorkdir: false,
ReuseContainers: false,
@@ -190,6 +190,7 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report.
Privileged: r.cfg.Container.Privileged,
DefaultActionInstance: taskContext["gitea_default_actions_url"].GetStringValue(),
PlatformPicker: r.labels.PickPlatform,
Vars: task.Vars,
}
rr, err := runner.New(runnerConfig)

View File

@@ -48,3 +48,6 @@ container:
privileged: false
# And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway).
options:
# The parent directory of a job's working directory.
# If it's empty, /workspace will be used.
workdir_parent:

View File

@@ -34,9 +34,10 @@ type Config struct {
Port uint16 `yaml:"port"`
} `yaml:"cache"`
Container struct {
NetworkMode string `yaml:"network_mode"`
Privileged bool `yaml:"privileged"`
Options string `yaml:"options"`
NetworkMode string `yaml:"network_mode"`
Privileged bool `yaml:"privileged"`
Options string `yaml:"options"`
WorkdirParent string `yaml:"workdir_parent"`
} `yaml:"container"`
}
@@ -94,6 +95,9 @@ func LoadDefault(file string) (*Config, error) {
if cfg.Container.NetworkMode == "" {
cfg.Container.NetworkMode = "bridge"
}
if cfg.Container.WorkdirParent == "" {
cfg.Container.WorkdirParent = "workspace"
}
if cfg.Runner.FetchTimeout <= 0 {
cfg.Runner.FetchTimeout = 5 * time.Second
}

View File

@@ -6,6 +6,7 @@ package report
import (
"context"
"fmt"
"regexp"
"strings"
"sync"
"time"
@@ -31,10 +32,14 @@ type Reporter struct {
logOffset int
logRows []*runnerv1.LogRow
logReplacer *strings.Replacer
oldnew []string
state *runnerv1.TaskState
stateMu sync.RWMutex
outputs sync.Map
debugOutputEnabled bool
stopCommandEndToken string
}
func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.Client, task *runnerv1.Task) *Reporter {
@@ -46,15 +51,22 @@ func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.C
oldnew = append(oldnew, v, "***")
}
return &Reporter{
rv := &Reporter{
ctx: ctx,
cancel: cancel,
client: client,
oldnew: oldnew,
logReplacer: strings.NewReplacer(oldnew...),
state: &runnerv1.TaskState{
Id: task.Id,
},
}
if task.Secrets["ACTIONS_STEP_DEBUG"] == "true" {
rv.debugOutputEnabled = true
}
return rv
}
func (r *Reporter) ResetSteps(l int) {
@@ -71,6 +83,13 @@ func (r *Reporter) Levels() []log.Level {
return log.AllLevels
}
func appendIfNotNil[T any](s []*T, v *T) []*T {
if v != nil {
return append(s, v)
}
return s
}
func (r *Reporter) Fire(entry *log.Entry) error {
r.stateMu.Lock()
defer r.stateMu.Unlock()
@@ -97,7 +116,7 @@ func (r *Reporter) Fire(entry *log.Entry) error {
}
}
if !r.duringSteps() {
r.logRows = append(r.logRows, r.parseLogRow(entry))
r.logRows = appendIfNotNil(r.logRows, r.parseLogRow(entry))
}
return nil
}
@@ -110,7 +129,7 @@ func (r *Reporter) Fire(entry *log.Entry) error {
}
if step == nil {
if !r.duringSteps() {
r.logRows = append(r.logRows, r.parseLogRow(entry))
r.logRows = appendIfNotNil(r.logRows, r.parseLogRow(entry))
}
return nil
}
@@ -124,10 +143,10 @@ func (r *Reporter) Fire(entry *log.Entry) error {
step.LogIndex = int64(r.logOffset + len(r.logRows))
}
step.LogLength++
r.logRows = append(r.logRows, r.parseLogRow(entry))
r.logRows = appendIfNotNil(r.logRows, r.parseLogRow(entry))
}
} else if !r.duringSteps() {
r.logRows = append(r.logRows, r.parseLogRow(entry))
r.logRows = appendIfNotNil(r.logRows, r.parseLogRow(entry))
}
if v, ok := entry.Data["stepResult"]; ok {
if stepResult, ok := r.parseResult(v); ok {
@@ -338,11 +357,70 @@ func (r *Reporter) parseResult(result interface{}) (runnerv1.Result, bool) {
return ret, ok
}
var cmdRegex = regexp.MustCompile(`^::([^ :]+)( .*)?::(.*)$`)
func (r *Reporter) handleCommand(originalContent, command, parameters, value string) *string {
if r.stopCommandEndToken != "" && command != r.stopCommandEndToken {
return &originalContent
}
switch command {
case "add-mask":
r.addMask(value)
return nil
case "debug":
if r.debugOutputEnabled {
return &value
}
return nil
case "notice":
// Not implemented yet, so just return the original content.
return &originalContent
case "warning":
// Not implemented yet, so just return the original content.
return &originalContent
case "error":
// Not implemented yet, so just return the original content.
return &originalContent
case "group":
// Returning the original content, because I think the frontend
// will use it when rendering the output.
return &originalContent
case "endgroup":
// Ditto
return &originalContent
case "stop-commands":
r.stopCommandEndToken = value
return nil
case r.stopCommandEndToken:
r.stopCommandEndToken = ""
return nil
}
return &originalContent
}
func (r *Reporter) parseLogRow(entry *log.Entry) *runnerv1.LogRow {
content := strings.TrimRightFunc(entry.Message, func(r rune) bool { return r == '\r' || r == '\n' })
matches := cmdRegex.FindStringSubmatch(content)
if matches != nil {
if output := r.handleCommand(content, matches[1], matches[2], matches[3]); output != nil {
content = *output
} else {
return nil
}
}
content = r.logReplacer.Replace(content)
return &runnerv1.LogRow{
Time: timestamppb.New(entry.Time),
Content: content,
}
}
func (r *Reporter) addMask(msg string) {
r.oldnew = append(r.oldnew, msg, "***")
r.logReplacer = strings.NewReplacer(r.oldnew...)
}

View File

@@ -0,0 +1,148 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package report
import (
"strings"
"testing"
log "github.com/sirupsen/logrus"
"gotest.tools/v3/assert"
)
func TestReporter_parseLogRow(t *testing.T) {
tests := []struct {
name string
debugOutputEnabled bool
args []string
want []string
}{
{
"No command", false,
[]string{"Hello, world!"},
[]string{"Hello, world!"},
},
{
"Add-mask", false,
[]string{
"foo mysecret bar",
"::add-mask::mysecret",
"foo mysecret bar",
},
[]string{
"foo mysecret bar",
"<nil>",
"foo *** bar",
},
},
{
"Debug enabled", true,
[]string{
"::debug::GitHub Actions runtime token access controls",
},
[]string{
"GitHub Actions runtime token access controls",
},
},
{
"Debug not enabled", false,
[]string{
"::debug::GitHub Actions runtime token access controls",
},
[]string{
"<nil>",
},
},
{
"notice", false,
[]string{
"::notice file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
},
[]string{
"::notice file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
},
},
{
"warning", false,
[]string{
"::warning file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
},
[]string{
"::warning file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
},
},
{
"error", false,
[]string{
"::error file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
},
[]string{
"::error file=file.name,line=42,endLine=48,title=Cool Title::Gosh, that's not going to work",
},
},
{
"group", false,
[]string{
"::group::",
"::endgroup::",
},
[]string{
"::group::",
"::endgroup::",
},
},
{
"stop-commands", false,
[]string{
"::add-mask::foo",
"::stop-commands::myverycoolstoptoken",
"::add-mask::bar",
"::debug::Stuff",
"myverycoolstoptoken",
"::add-mask::baz",
"::myverycoolstoptoken::",
"::add-mask::wibble",
"foo bar baz wibble",
},
[]string{
"<nil>",
"<nil>",
"::add-mask::bar",
"::debug::Stuff",
"myverycoolstoptoken",
"::add-mask::baz",
"<nil>",
"<nil>",
"*** bar baz ***",
},
},
{
"unknown command", false,
[]string{
"::set-mask::foo",
},
[]string{
"::set-mask::foo",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Reporter{
logReplacer: strings.NewReplacer(),
debugOutputEnabled: tt.debugOutputEnabled,
}
for idx, arg := range tt.args {
rv := r.parseLogRow(&log.Entry{Message: arg})
got := "<nil>"
if rv != nil {
got = rv.Content
}
assert.Equal(t, tt.want[idx], got)
}
})
}
}