forked from NYANDEV/forgejo
Compare commits
18 commits
forgejo
...
forgejo-ci
Author | SHA1 | Date | |
---|---|---|---|
|
e1e08786ec | ||
|
44a3ba1fcf | ||
|
67f92dc9ab | ||
|
083329e0a0 | ||
|
82177a0f66 | ||
|
e7758aaa94 | ||
|
205512ef96 | ||
|
c996d51bd6 | ||
|
17e91c96e3 | ||
|
b7f02b9846 | ||
|
239df83859 | ||
|
7b54fe01c2 | ||
|
c2acb181c5 | ||
|
8228adfd26 | ||
|
427b059996 | ||
|
3ebab73c4e | ||
|
89705502c4 | ||
|
9ba069327d |
46 changed files with 1808 additions and 691 deletions
154
.forgejo/actions/build-release/action.yml
Normal file
154
.forgejo/actions/build-release/action.yml
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
name: 'Build release'
|
||||||
|
author: 'Forgejo authors'
|
||||||
|
description: |
|
||||||
|
Build release
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
forgejo:
|
||||||
|
description: 'URL of the Forgejo instance where the release is uploaded'
|
||||||
|
required: true
|
||||||
|
owner:
|
||||||
|
description: 'User or organization where the release is uploaded, relative to the Forgejo instance'
|
||||||
|
required: true
|
||||||
|
repository:
|
||||||
|
description: 'Repository where the release is uploaded, relative to the owner'
|
||||||
|
required: true
|
||||||
|
doer:
|
||||||
|
description: 'Name of the user authoring the release'
|
||||||
|
required: true
|
||||||
|
tag-version:
|
||||||
|
description: 'Version of the release derived from the tag withint the leading v'
|
||||||
|
required: true
|
||||||
|
suffix:
|
||||||
|
description: 'Suffix to add to the image tag'
|
||||||
|
token:
|
||||||
|
description: 'token'
|
||||||
|
required: true
|
||||||
|
dockerfile:
|
||||||
|
description: 'path to the dockerfile'
|
||||||
|
default: 'Dockerfile'
|
||||||
|
platforms:
|
||||||
|
description: 'Coma separated list of platforms'
|
||||||
|
default: 'linux/amd64,linux/arm64'
|
||||||
|
release-notes:
|
||||||
|
description: 'Full text of the release notes'
|
||||||
|
default: 'Release notes placeholder'
|
||||||
|
binary-name:
|
||||||
|
description: 'Name of the binary'
|
||||||
|
binary-path:
|
||||||
|
description: 'Path of the binary within the container to extract into binary-name'
|
||||||
|
verbose:
|
||||||
|
description: 'Increase the verbosity level'
|
||||||
|
default: 'false'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- run: echo "${{ github.action_path }}" >> $GITHUB_PATH
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt-get install -y -qq xz-utils
|
||||||
|
|
||||||
|
- name: set -x if verbose is required
|
||||||
|
id: verbose
|
||||||
|
run: |
|
||||||
|
if ${{ inputs.verbose }} ; then
|
||||||
|
echo "shell=set -x" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create the insecure and buildx-config variables for the container registry
|
||||||
|
id: registry
|
||||||
|
run: |
|
||||||
|
${{ steps.verbose.outputs.shell }}
|
||||||
|
url="${{ inputs.forgejo }}"
|
||||||
|
hostport=${url##http*://}
|
||||||
|
hostport=${hostport%%/}
|
||||||
|
echo "host-port=${hostport}" >> "$GITHUB_OUTPUT"
|
||||||
|
if ! [[ $url =~ ^http:// ]] ; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
cat >> "$GITHUB_OUTPUT" <<EOF
|
||||||
|
insecure=true
|
||||||
|
buildx-config<<ENDVAR
|
||||||
|
[registry."${hostport}"]
|
||||||
|
http = true
|
||||||
|
ENDVAR
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Allow docker pull/push to forgejo
|
||||||
|
if: ${{ steps.registry.outputs.insecure }}
|
||||||
|
run: |-
|
||||||
|
mkdir -p /etc/docker
|
||||||
|
cat > /etc/docker/daemon.json <<EOF
|
||||||
|
{
|
||||||
|
"insecure-registries" : ["${{ steps.registry.outputs.host-port }}"],
|
||||||
|
"bip": "172.26.0.1/16"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Install docker
|
||||||
|
run: |
|
||||||
|
echo deb http://deb.debian.org/debian bullseye-backports main | tee /etc/apt/sources.list.d/backports.list && apt-get -qq update
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -qq -y -t bullseye-backports docker.io
|
||||||
|
|
||||||
|
- uses: https://github.com/docker/setup-buildx-action@v2
|
||||||
|
with:
|
||||||
|
config-inline: |
|
||||||
|
${{ steps.registry.outputs.buildx-config }}
|
||||||
|
|
||||||
|
- name: Login to the container registry
|
||||||
|
run: |
|
||||||
|
BASE64_AUTH=`echo -n "${{ inputs.doer }}:${{ inputs.token }}" | base64 -w0`
|
||||||
|
mkdir -p ~/.docker
|
||||||
|
echo "{\"auths\": {\"$CI_REGISTRY\": {\"auth\": \"$BASE64_AUTH\"}}}" > ~/.docker/config.json
|
||||||
|
env:
|
||||||
|
CI_REGISTRY: "${{ steps.registry.outputs.host-port }}"
|
||||||
|
|
||||||
|
- name: Build the container image for each architecture
|
||||||
|
uses: https://github.com/docker/build-push-action@v4
|
||||||
|
# workaround until https://github.com/docker/build-push-action/commit/d8823bfaed2a82c6f5d4799a2f8e86173c461aba is in @v4 or @v5 is released
|
||||||
|
env:
|
||||||
|
ACTIONS_RUNTIME_TOKEN: ''
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
file: ${{ inputs.dockerfile }}
|
||||||
|
platforms: ${{ inputs.platforms }}
|
||||||
|
tags: ${{ steps.registry.outputs.host-port }}/${{ inputs.owner }}/${{ inputs.repository }}:${{ inputs.tag-version }}${{ inputs.suffix }}
|
||||||
|
|
||||||
|
- name: Extract the binary from the container images into the release directory
|
||||||
|
if: inputs.binary-name != ''
|
||||||
|
run: |
|
||||||
|
${{ steps.verbose.outputs.shell }}
|
||||||
|
mkdir -p release
|
||||||
|
cd release
|
||||||
|
for platform in $(echo ${{ inputs.platforms }} | tr ',' ' '); do
|
||||||
|
arch=$(echo $platform | sed -e 's|linux/||g' -e 's|arm/v6|arm-6|g')
|
||||||
|
docker create --platform $platform --name forgejo-$arch ${{ steps.registry.outputs.host-port }}/${{ inputs.owner }}/${{ inputs.repository }}:${{ inputs.tag-version }}${{ inputs.suffix }}
|
||||||
|
binary="${{ inputs.binary-name }}-${{ inputs.tag-version }}-linux"
|
||||||
|
docker cp forgejo-$arch:${{ inputs.binary-path }} $binary-$arch
|
||||||
|
chmod +x $binary-$arch
|
||||||
|
# the displayed version has a + instead of the first -, deal with it
|
||||||
|
pattern=$(echo "${{ inputs.tag-version }}" | tr - .)
|
||||||
|
if ! ./$binary-$arch --version | grep "$pattern" ; then
|
||||||
|
echo "ERROR: expected version pattern $pattern not found in the output of $binary-$arch --version"
|
||||||
|
./$binary-$arch --version
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
xz --keep -9 $binary-$arch
|
||||||
|
shasum -a 256 $binary-$arch > $binary-$arch.sha256
|
||||||
|
shasum -a 256 $binary-$arch.xz > $binary-$arch.xz.sha256
|
||||||
|
docker rm forgejo-$arch
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: publish release
|
||||||
|
if: inputs.binary-name != ''
|
||||||
|
uses: https://code.forgejo.org/actions/forgejo-release@v1
|
||||||
|
with:
|
||||||
|
direction: upload
|
||||||
|
release-dir: release
|
||||||
|
release-notes: "${{ inputs.release-notes }}"
|
||||||
|
token: ${{ inputs.token }}
|
||||||
|
verbose: ${{ steps.verbose.outputs.value }}
|
110
.forgejo/actions/publish-release/action.yml
Normal file
110
.forgejo/actions/publish-release/action.yml
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
name: 'Publish release'
|
||||||
|
author: 'Forgejo authors'
|
||||||
|
description: |
|
||||||
|
Publish release
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
forgejo:
|
||||||
|
description: 'URL of the Forgejo instance where the release is uploaded (e.g. https://codeberg.org)'
|
||||||
|
required: true
|
||||||
|
from-owner:
|
||||||
|
description: 'the owner from which a release is to be copied (e.g forgejo-integration)'
|
||||||
|
required: true
|
||||||
|
to-owner:
|
||||||
|
description: 'the owner to which a release is to be copied (e.g. forgejo-experimental). It has be an organization in which doer has the required permissions. Or be the same as the doer'
|
||||||
|
required: true
|
||||||
|
repo:
|
||||||
|
description: 'the repository from which a release is to be copied relative to from-owner and to-owner'
|
||||||
|
default: 'forgejo'
|
||||||
|
ref-name:
|
||||||
|
description: 'ref_name of the tag of the release to be copied (e.g. github.ref_name)'
|
||||||
|
required: true
|
||||||
|
doer:
|
||||||
|
description: 'Name of the user authoring the release (e.g. release-team). The user must be authorized to create packages in to-owner and releases in to-owner/repo'
|
||||||
|
required: true
|
||||||
|
token:
|
||||||
|
description: 'application token created on forgejo by the doer, with a scope allowing it to create packages in to-owner and releases in to-owner/repo'
|
||||||
|
required: true
|
||||||
|
gpg-private-key:
|
||||||
|
description: 'GPG Private Key to sign the release artifacts'
|
||||||
|
gpg-passphrase:
|
||||||
|
description: 'Passphrase of the GPG Private Key'
|
||||||
|
verbose:
|
||||||
|
description: 'Increase the verbosity level'
|
||||||
|
default: 'false'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- id: hostport
|
||||||
|
run: |
|
||||||
|
url="${{ inputs.forgejo }}"
|
||||||
|
hostport=${url##http*://}
|
||||||
|
hostport=${hostport%%/}
|
||||||
|
echo "value=$hostport" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- id: tag-version
|
||||||
|
run: |
|
||||||
|
version="${{ inputs.ref-name }}"
|
||||||
|
version=${version##*v}
|
||||||
|
echo "value=$version" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create the release notes
|
||||||
|
id: release-notes
|
||||||
|
run: |
|
||||||
|
anchor=${{ steps.tag-version.outputs.value }}
|
||||||
|
anchor=${anchor//./-}
|
||||||
|
cat >> "$GITHUB_OUTPUT" <<EOF
|
||||||
|
value<<ENDVAR
|
||||||
|
See https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#$anchor
|
||||||
|
ENDVAR
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: apt-get install docker.io
|
||||||
|
run: |
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -qq -y docker.io
|
||||||
|
|
||||||
|
- name: download release
|
||||||
|
uses: https://code.forgejo.org/actions/forgejo-release@v1
|
||||||
|
with:
|
||||||
|
url: ${{ inputs.forgejo }}
|
||||||
|
repo: ${{ inputs.from-owner }}/${{ inputs.repo }}
|
||||||
|
direction: download
|
||||||
|
release-dir: release
|
||||||
|
download-retry: 60
|
||||||
|
token: ${{ inputs.token }}
|
||||||
|
verbose: ${{ inputs.verbose }}
|
||||||
|
|
||||||
|
- name: upload release
|
||||||
|
uses: https://code.forgejo.org/actions/forgejo-release@v1
|
||||||
|
with:
|
||||||
|
url: ${{ inputs.forgejo }}
|
||||||
|
repo: ${{ inputs.to-owner }}/${{ inputs.repo }}
|
||||||
|
direction: upload
|
||||||
|
release-dir: release
|
||||||
|
release-notes: ${{ steps.release-notes.outputs.value }}
|
||||||
|
token: ${{ inputs.token }}
|
||||||
|
gpg-private-key: ${{ inputs.gpg-private-key }}
|
||||||
|
gpg-passphrase: ${{ inputs.gpg-passphrase }}
|
||||||
|
verbose: ${{ inputs.verbose }}
|
||||||
|
|
||||||
|
- name: login to the registry
|
||||||
|
uses: https://github.com/docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ${{ steps.hostport.outputs.value }}
|
||||||
|
username: ${{ inputs.doer }}
|
||||||
|
password: ${{ inputs.token }}
|
||||||
|
|
||||||
|
- uses: https://code.forgejo.org/forgejo/forgejo-container-image@v1
|
||||||
|
env:
|
||||||
|
VERIFY: 'false'
|
||||||
|
with:
|
||||||
|
url: https://${{ steps.hostport.outputs.value }}
|
||||||
|
destination-owner: ${{ inputs.to-owner }}
|
||||||
|
owner: ${{ inputs.from-owner }}
|
||||||
|
suffixes: '-rootless'
|
||||||
|
project: ${{ inputs.repo }}
|
||||||
|
tag: ${{ steps.tag-version.outputs.value }}
|
||||||
|
doer: ${{ inputs.doer }}
|
||||||
|
token: ${{ inputs.token }}
|
||||||
|
verbose: ${{ inputs.verbose }}
|
16
.forgejo/cascading-pr-end-to-end
Executable file
16
.forgejo/cascading-pr-end-to-end
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
end_to_end=$1
|
||||||
|
end_to_end_pr=$2
|
||||||
|
forgejo=$3
|
||||||
|
forgejo_pr=$4
|
||||||
|
|
||||||
|
url=$(jq --raw-output .head.repo.html_url < $forgejo_pr)
|
||||||
|
test "$url" != null
|
||||||
|
branch=$(jq --raw-output .head.ref < $forgejo_pr)
|
||||||
|
test "$branch" != null
|
||||||
|
cd $end_to_end
|
||||||
|
echo $url $branch 7.0.0+0-gitea-1.22.0 > forgejo/sources/1.22
|
||||||
|
date > last-upgrade
|
3
.forgejo/testdata/build-release/Dockerfile
vendored
Normal file
3
.forgejo/testdata/build-release/Dockerfile
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
FROM public.ecr.aws/docker/library/alpine:3.18
|
||||||
|
RUN mkdir -p /app/gitea
|
||||||
|
RUN ( echo '#!/bin/sh' ; echo "echo forgejo v1.2.3" ) > /app/gitea/gitea ; chmod +x /app/gitea/gitea
|
5
.forgejo/testdata/build-release/Makefile
vendored
Normal file
5
.forgejo/testdata/build-release/Makefile
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
VERSION ?= $(shell cat VERSION 2>/dev/null)
|
||||||
|
sources-tarbal:
|
||||||
|
mkdir -p dist/release
|
||||||
|
echo $(VERSION) > VERSION
|
||||||
|
sources=forgejo-src-$(VERSION).tar.gz ; tar --transform 's|^./|forgejo-src-$(VERSION)/|' -czf dist/release/forgejo-src-$(VERSION).tar.gz . ; cd dist/release ; shasum -a 256 $$sources > $$sources.sha256
|
0
.forgejo/testdata/build-release/modules/public/bindata.go
vendored
Normal file
0
.forgejo/testdata/build-release/modules/public/bindata.go
vendored
Normal file
0
.forgejo/testdata/build-release/public/assets/css/placeholder
vendored
Normal file
0
.forgejo/testdata/build-release/public/assets/css/placeholder
vendored
Normal file
0
.forgejo/testdata/build-release/public/assets/fonts/placeholder
vendored
Normal file
0
.forgejo/testdata/build-release/public/assets/fonts/placeholder
vendored
Normal file
0
.forgejo/testdata/build-release/public/assets/js/placeholder
vendored
Normal file
0
.forgejo/testdata/build-release/public/assets/js/placeholder
vendored
Normal file
99
.forgejo/workflows/build-release-integration.yml
Normal file
99
.forgejo/workflows/build-release-integration.yml
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
name: Integration tests for the release process
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- Makefile
|
||||||
|
- Dockerfile
|
||||||
|
- Dockerfile.rootless
|
||||||
|
- docker/**
|
||||||
|
- .forgejo/actions/build-release/action.yml
|
||||||
|
- .forgejo/workflows/build-release.yml
|
||||||
|
- .forgejo/workflows/build-release-integration.yml
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-simulation:
|
||||||
|
runs-on: self-hosted
|
||||||
|
if: secrets.ROLE != 'forgejo-integration' && secrets.ROLE != 'forgejo-experimental' && secrets.ROLE != 'forgejo-release'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- id: forgejo
|
||||||
|
uses: https://code.forgejo.org/actions/setup-forgejo@v1
|
||||||
|
with:
|
||||||
|
user: root
|
||||||
|
password: admin1234
|
||||||
|
image-version: 1.19
|
||||||
|
lxc-ip-prefix: 10.0.9
|
||||||
|
|
||||||
|
- name: publish the forgejo release
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
|
||||||
|
version=1.2.3
|
||||||
|
cat > /etc/docker/daemon.json <<EOF
|
||||||
|
{
|
||||||
|
"insecure-registries" : ["${{ steps.forgejo.outputs.host-port }}"]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
systemctl restart docker
|
||||||
|
|
||||||
|
apt-get install -qq -y xz-utils
|
||||||
|
|
||||||
|
dir=$(mktemp -d)
|
||||||
|
trap "rm -fr $dir" EXIT
|
||||||
|
|
||||||
|
url=http://root:admin1234@${{ steps.forgejo.outputs.host-port }}
|
||||||
|
export FORGEJO_RUNNER_LOGS="${{ steps.forgejo.outputs.runner-logs }}"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Create a new project with a fake forgejo and the release workflow only
|
||||||
|
#
|
||||||
|
cp -a .forgejo/testdata/build-release/* $dir
|
||||||
|
mkdir -p $dir/.forgejo/workflows
|
||||||
|
cp .forgejo/workflows/build-release.yml $dir/.forgejo/workflows
|
||||||
|
cp -a .forgejo/actions $dir/.forgejo/actions
|
||||||
|
cp $dir/Dockerfile $dir/Dockerfile.rootless
|
||||||
|
|
||||||
|
forgejo-test-helper.sh push $dir $url root forgejo
|
||||||
|
sha=$(forgejo-test-helper.sh branch_tip $url root/forgejo main)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Push a tag to trigger the release workflow and wait for it to complete
|
||||||
|
#
|
||||||
|
forgejo-curl.sh api_json --data-raw '{"tag_name": "v'$version'", "target": "'$sha'"}' $url/api/v1/repos/root/forgejo/tags
|
||||||
|
LOOPS=180 forgejo-test-helper.sh wait_success "$url" root/forgejo $sha
|
||||||
|
|
||||||
|
#
|
||||||
|
# uncomment to see the logs even when everything is reported to be working ok
|
||||||
|
#
|
||||||
|
#cat $FORGEJO_RUNNER_LOGS
|
||||||
|
|
||||||
|
#
|
||||||
|
# Minimal sanity checks. e2e test is for the setup-forgejo
|
||||||
|
# action and the infrastructure playbook. Since the binary
|
||||||
|
# is a script shell it does not test the sanity of the cross
|
||||||
|
# build, only the sanity of the naming of the binaries.
|
||||||
|
#
|
||||||
|
for arch in amd64 arm64 arm-6 ; do
|
||||||
|
binary=forgejo-$version-linux-$arch
|
||||||
|
for suffix in '' '.xz' ; do
|
||||||
|
curl --fail -L -sS $url/root/forgejo/releases/download/v$version/$binary$suffix > $binary$suffix
|
||||||
|
if test "$suffix" = .xz ; then
|
||||||
|
unxz --keep $binary$suffix
|
||||||
|
fi
|
||||||
|
chmod +x $binary
|
||||||
|
./$binary --version | grep $version
|
||||||
|
curl --fail -L -sS $url/root/forgejo/releases/download/v$version/$binary$suffix.sha256 > $binary$suffix.sha256
|
||||||
|
shasum -a 256 --check $binary$suffix.sha256
|
||||||
|
rm $binary$suffix
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
sources=forgejo-src-$version.tar.gz
|
||||||
|
curl --fail -L -sS $url/root/forgejo/releases/download/v$version/$sources > $sources
|
||||||
|
curl --fail -L -sS $url/root/forgejo/releases/download/v$version/$sources.sha256 > $sources.sha256
|
||||||
|
shasum -a 256 --check $sources.sha256
|
||||||
|
|
||||||
|
docker pull ${{ steps.forgejo.outputs.host-port }}/root/forgejo:$version
|
||||||
|
docker pull ${{ steps.forgejo.outputs.host-port }}/root/forgejo:$version-rootless
|
190
.forgejo/workflows/build-release.yml
Normal file
190
.forgejo/workflows/build-release.yml
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
name: Build release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: self-hosted
|
||||||
|
# root is used for testing, allow it
|
||||||
|
if: secrets.ROLE == 'forgejo-integration' || github.repository_owner == 'root'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Increase the verbosity when there are no secrets
|
||||||
|
id: verbose
|
||||||
|
run: |
|
||||||
|
if test -z "${{ secrets.TOKEN }}"; then
|
||||||
|
value=true
|
||||||
|
else
|
||||||
|
value=false
|
||||||
|
fi
|
||||||
|
echo "value=$value" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Sanitize the name of the repository
|
||||||
|
id: repository
|
||||||
|
run: |
|
||||||
|
set -x # comment out
|
||||||
|
repository="${{ github.repository }}"
|
||||||
|
echo "value=${repository##*/}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: When in a test environment, create a token
|
||||||
|
id: token
|
||||||
|
if: ${{ secrets.TOKEN == '' }}
|
||||||
|
run: |
|
||||||
|
apt-get -qq install -y jq
|
||||||
|
url="${{ env.GITHUB_SERVER_URL }}"
|
||||||
|
hostport=${url##http*://}
|
||||||
|
hostport=${hostport%%/}
|
||||||
|
doer=root
|
||||||
|
api=http://$doer:admin1234@$hostport/api/v1/users/$doer/tokens
|
||||||
|
curl -sS -X DELETE $api/release
|
||||||
|
token=$(curl -sS -X POST -H 'Content-Type: application/json' --data-raw '{"name": "release", "scopes": ["all"]}' $api | jq --raw-output .sha1)
|
||||||
|
echo "value=${token}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: ">=1.20"
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Create the version from ref_name
|
||||||
|
id: tag-version
|
||||||
|
run: |
|
||||||
|
version="${{ github.ref_name }}"
|
||||||
|
version=${version##*v}
|
||||||
|
echo "value=$version" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create the release notes
|
||||||
|
id: release-notes
|
||||||
|
run: |
|
||||||
|
cat >> "$GITHUB_OUTPUT" <<EOF
|
||||||
|
value<<ENDVAR
|
||||||
|
See https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#${{ steps.tag-version.outputs.value }}
|
||||||
|
ENDVAR
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Build sources
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
apt-get -qq install -y make
|
||||||
|
version=${{ steps.tag-version.outputs.value }}
|
||||||
|
#
|
||||||
|
# Make sure all files are owned by the current user.
|
||||||
|
# When run as root `npx webpack` will assume the identity
|
||||||
|
# of the owner of the current working directory and may
|
||||||
|
# fail to create files if some sub-directories are not owned
|
||||||
|
# by the same user.
|
||||||
|
#
|
||||||
|
# Binaries:
|
||||||
|
# Node: 18.17.0 - /usr/local/node-v18.17.0-linux-x64/bin/node
|
||||||
|
# npm: 9.6.7 - /usr/local/node-v18.17.0-linux-x64/bin/npm
|
||||||
|
# Packages:
|
||||||
|
# add-asset-webpack-plugin: 2.0.1 => 2.0.1
|
||||||
|
# css-loader: 6.8.1 => 6.8.1
|
||||||
|
# esbuild-loader: 3.0.1 => 3.0.1
|
||||||
|
# license-checker-webpack-plugin: 0.2.1 => 0.2.1
|
||||||
|
# monaco-editor-webpack-plugin: 7.0.1 => 7.0.1
|
||||||
|
# vue-loader: 17.2.2 => 17.2.2
|
||||||
|
# webpack: 5.87.0 => 5.87.0
|
||||||
|
# webpack-cli: 5.1.4 => 5.1.4
|
||||||
|
#
|
||||||
|
chown -R $(id -u) .
|
||||||
|
make VERSION=$version TAGS=bindata sources-tarbal
|
||||||
|
mv dist/release release
|
||||||
|
|
||||||
|
(
|
||||||
|
tmp=$(mktemp -d)
|
||||||
|
tar --directory $tmp -zxvf release/*$version*.tar.gz
|
||||||
|
cd $tmp/*
|
||||||
|
#
|
||||||
|
# Verify `make frontend` files are available
|
||||||
|
#
|
||||||
|
test -d public/assets/css
|
||||||
|
test -d public/assets/fonts
|
||||||
|
test -d public/assets/js
|
||||||
|
#
|
||||||
|
# Verify `make generate` files are available
|
||||||
|
#
|
||||||
|
test -f modules/public/bindata.go
|
||||||
|
#
|
||||||
|
# Sanity check to verify that the source tarbal knows the
|
||||||
|
# version and is able to rebuild itself from it.
|
||||||
|
#
|
||||||
|
# When in sources the version is determined with git.
|
||||||
|
# When in the tarbal the version is determined from a VERSION file.
|
||||||
|
#
|
||||||
|
make sources-tarbal
|
||||||
|
tarbal=$(echo dist/release/*$version*.tar.gz)
|
||||||
|
if ! test -f $tarbal ; then
|
||||||
|
echo $tarbal does not exist
|
||||||
|
find dist release
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
- name: build container & release (when TOKEN secret is not set)
|
||||||
|
if: ${{ secrets.TOKEN == '' }}
|
||||||
|
uses: ./.forgejo/actions/build-release
|
||||||
|
with:
|
||||||
|
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||||
|
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||||
|
repository: "${{ steps.repository.outputs.value }}"
|
||||||
|
doer: root
|
||||||
|
tag-version: "${{ steps.tag-version.outputs.value }}"
|
||||||
|
token: ${{ steps.token.outputs.value }}
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v6
|
||||||
|
release-notes: "${{ steps.release-notes.outputs.value }}"
|
||||||
|
binary-name: forgejo
|
||||||
|
binary-path: /app/gitea/gitea
|
||||||
|
verbose: ${{ steps.verbose.outputs.value }}
|
||||||
|
|
||||||
|
- name: build rootless container (when TOKEN secret is not set)
|
||||||
|
if: ${{ secrets.TOKEN == '' }}
|
||||||
|
uses: ./.forgejo/actions/build-release
|
||||||
|
with:
|
||||||
|
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||||
|
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||||
|
repository: "${{ steps.repository.outputs.value }}"
|
||||||
|
doer: root
|
||||||
|
tag-version: "${{ steps.tag-version.outputs.value }}"
|
||||||
|
token: ${{ steps.token.outputs.value }}
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v6
|
||||||
|
suffix: -rootless
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
verbose: ${{ steps.verbose.outputs.value }}
|
||||||
|
|
||||||
|
- name: build container & release (when TOKEN secret is set)
|
||||||
|
if: ${{ secrets.TOKEN != '' }}
|
||||||
|
uses: ./.forgejo/actions/build-release
|
||||||
|
with:
|
||||||
|
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||||
|
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||||
|
repository: "${{ steps.repository.outputs.value }}"
|
||||||
|
doer: "${{ secrets.DOER }}"
|
||||||
|
tag-version: "${{ steps.tag-version.outputs.value }}"
|
||||||
|
token: "${{ secrets.TOKEN }}"
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v6
|
||||||
|
release-notes: "${{ steps.release-notes.outputs.value }}"
|
||||||
|
binary-name: forgejo
|
||||||
|
binary-path: /app/gitea/gitea
|
||||||
|
verbose: ${{ steps.verbose.outputs.value }}
|
||||||
|
|
||||||
|
- name: build rootless container (when TOKEN secret is set)
|
||||||
|
if: ${{ secrets.TOKEN != '' }}
|
||||||
|
uses: ./.forgejo/actions/build-release
|
||||||
|
with:
|
||||||
|
forgejo: "${{ env.GITHUB_SERVER_URL }}"
|
||||||
|
owner: "${{ env.GITHUB_REPOSITORY_OWNER }}"
|
||||||
|
repository: "${{ steps.repository.outputs.value }}"
|
||||||
|
doer: "${{ secrets.DOER }}"
|
||||||
|
tag-version: "${{ steps.tag-version.outputs.value }}"
|
||||||
|
token: "${{ secrets.TOKEN }}"
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v6
|
||||||
|
suffix: -rootless
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
verbose: ${{ steps.verbose.outputs.value }}
|
52
.forgejo/workflows/cascade-setup-end-to-end.yml
Normal file
52
.forgejo/workflows/cascade-setup-end-to-end.yml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
- labeled
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
info:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: node:20-bookworm
|
||||||
|
steps:
|
||||||
|
- name: event
|
||||||
|
run: |
|
||||||
|
echo github.event.pull_request.head.repo.fork = ${{ github.event.pull_request.head.repo.fork }}
|
||||||
|
echo github.event.action = ${{ github.event.action }}
|
||||||
|
echo github.event.pull_request.merged = ${{ github.event.pull_request.merged }}
|
||||||
|
echo github.event.pull_request.labels.*.name
|
||||||
|
cat <<'EOF'
|
||||||
|
${{ toJSON(github.event.pull_request.labels.*.name) }}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cascade:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: node:20-bookworm
|
||||||
|
if: >
|
||||||
|
github.event.pull_request.head.repo.fork == false &&
|
||||||
|
(
|
||||||
|
(
|
||||||
|
github.event.action == 'closed' && github.event.pull_request.merged
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'ready-to-merge')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/cascading-pr@v1
|
||||||
|
with:
|
||||||
|
origin-url: ${{ env.GITHUB_SERVER_URL }}
|
||||||
|
origin-repo: ${{ github.repository }}
|
||||||
|
origin-token: ${{ secrets.END_TO_END_CASCADING_PR_ORIGIN }}
|
||||||
|
origin-pr: ${{ github.event.pull_request.number }}
|
||||||
|
destination-url: https://code.forgejo.org
|
||||||
|
destination-fork-repo: cascading-pr/end-to-end
|
||||||
|
destination-repo: forgejo/end-to-end
|
||||||
|
destination-branch: main
|
||||||
|
destination-token: ${{ secrets.END_TO_END_CASCADING_PR_DESTINATION }}
|
||||||
|
close-merge: true
|
||||||
|
update: .forgejo/cascading-pr-end-to-end
|
79
.forgejo/workflows/publish-release.yml
Normal file
79
.forgejo/workflows/publish-release.yml
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# See also https://forgejo.org/docs/next/developer/RELEASE/#release-process
|
||||||
|
#
|
||||||
|
# https://codeberg.org/forgejo-experimental/forgejo
|
||||||
|
#
|
||||||
|
# Copies a release from codeberg.org/forgejo-integration to codeberg.org/forgejo-experimental
|
||||||
|
#
|
||||||
|
# ROLE: forgejo-experimental
|
||||||
|
# FORGEJO: https://codeberg.org
|
||||||
|
# FROM_OWNER: forgejo-integration
|
||||||
|
# TO_OWNER: forgejo-experimental
|
||||||
|
# DOER: forgejo-experimental-ci
|
||||||
|
# TOKEN: <generated from codeberg.org/forgejo-experimental-ci>
|
||||||
|
#
|
||||||
|
# https://forgejo.octopuce.forgejo.org/forgejo/forgejo
|
||||||
|
#
|
||||||
|
# Copies & sign a release from codeberg.org/forgejo-integration to codeberg.org/forgejo
|
||||||
|
#
|
||||||
|
# ROLE: forgejo-release
|
||||||
|
# FORGEJO: https://codeberg.org
|
||||||
|
# FROM_OWNER: forgejo-integration
|
||||||
|
# TO_OWNER: forgejo
|
||||||
|
# DOER: release-team
|
||||||
|
# TOKEN: <generated from codeberg.org/release-team>
|
||||||
|
# GPG_PRIVATE_KEY: <XYZ>
|
||||||
|
# GPG_PASSPHRASE: <ABC>
|
||||||
|
#
|
||||||
|
name: Pubish release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: self-hosted
|
||||||
|
if: secrets.DOER != '' && secrets.FORGEJO != '' && secrets.TO_OWNER != '' && secrets.FROM_OWNER != '' && secrets.TOKEN != ''
|
||||||
|
steps:
|
||||||
|
- name: install the certificate authority
|
||||||
|
if: secrets.ROLE == 'forgejo-release'
|
||||||
|
run: |
|
||||||
|
apt-get install -qq -y wget
|
||||||
|
wget --no-check-certificate -O /usr/local/share/ca-certificates/enough.crt https://forgejo.octopuce.forgejo.org/forgejo/enough/raw/branch/main/certs/2023-05-13/ca.crt
|
||||||
|
update-ca-certificates --fresh
|
||||||
|
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: copy & sign binaries and container images from one owner to another
|
||||||
|
uses: ./.forgejo/actions/publish-release
|
||||||
|
with:
|
||||||
|
forgejo: ${{ secrets.FORGEJO }}
|
||||||
|
from-owner: ${{ secrets.FROM_OWNER }}
|
||||||
|
to-owner: ${{ secrets.TO_OWNER }}
|
||||||
|
ref-name: ${{ github.ref_name }}
|
||||||
|
doer: ${{ secrets.DOER }}
|
||||||
|
token: ${{ secrets.TOKEN }}
|
||||||
|
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||||
|
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||||
|
verbose: ${{ secrets.VERBOSE }}
|
||||||
|
|
||||||
|
|
||||||
|
- name: set up go for the DNS update below
|
||||||
|
uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
if: secrets.ROLE == 'forgejo-experimental'
|
||||||
|
with:
|
||||||
|
go-version: ">=1.21"
|
||||||
|
check-latest: true
|
||||||
|
- name: update the _release.experimental DNS record
|
||||||
|
if: secrets.ROLE == 'forgejo-experimental'
|
||||||
|
uses: https://code.forgejo.org/actions/ovh-dns-update@v1
|
||||||
|
with:
|
||||||
|
subdomain: _release.experimental
|
||||||
|
domain: forgejo.com # there is a CNAME from .org to .com (for security reasons)
|
||||||
|
record-id: 5283602601
|
||||||
|
value: v=${{ github.ref_name }}
|
||||||
|
ovh-app-key: ${{ secrets.OVH_APP_KEY }}
|
||||||
|
ovh-app-secret: ${{ secrets.OVH_APP_SECRET }}
|
||||||
|
ovh-consumer-key: ${{ secrets.OVH_CON_KEY }}
|
189
.forgejo/workflows/testing.yml
Normal file
189
.forgejo/workflows/testing.yml
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
name: testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'forgejo*'
|
||||||
|
- 'v*/forgejo*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-backend:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: 'docker.io/node:20-bookworm'
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: ">=1.21"
|
||||||
|
check-latest: true
|
||||||
|
- run: make deps-backend deps-tools
|
||||||
|
- run: make lint-backend
|
||||||
|
env:
|
||||||
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
|
checks-backend:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: 'docker.io/node:20-bookworm'
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: ">=1.21"
|
||||||
|
check-latest: true
|
||||||
|
- run: make deps-backend deps-tools
|
||||||
|
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
|
||||||
|
test-unit:
|
||||||
|
runs-on: docker
|
||||||
|
needs: [lint-backend, checks-backend]
|
||||||
|
container:
|
||||||
|
image: 'docker.io/node:20-bookworm'
|
||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: 'docker.io/bitnami/minio:2023.8.31'
|
||||||
|
env:
|
||||||
|
MINIO_ROOT_USER: 123456
|
||||||
|
MINIO_ROOT_PASSWORD: 12345678
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: ">=1.21"
|
||||||
|
- run: |
|
||||||
|
git config --add safe.directory '*'
|
||||||
|
adduser --quiet --comment forgejo --disabled-password forgejo
|
||||||
|
chown -R forgejo:forgejo .
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make deps-backend'
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make backend'
|
||||||
|
env:
|
||||||
|
TAGS: bindata
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make unit-test-coverage test-check'
|
||||||
|
timeout-minutes: 50
|
||||||
|
env:
|
||||||
|
RACE_ENABLED: 'true'
|
||||||
|
TAGS: bindata
|
||||||
|
test-mysql:
|
||||||
|
runs-on: docker
|
||||||
|
needs: [lint-backend, checks-backend]
|
||||||
|
container:
|
||||||
|
image: 'docker.io/node:20-bookworm'
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: 'docker.io/mysql:8-debian'
|
||||||
|
env:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
|
MYSQL_DATABASE: testgitea
|
||||||
|
#
|
||||||
|
# See also https://codeberg.org/forgejo/forgejo/issues/976
|
||||||
|
#
|
||||||
|
cmd: ['mysqld', '--innodb-adaptive-flushing=OFF', '--innodb-buffer-pool-size=4G', '--innodb-log-buffer-size=128M', '--innodb-flush-log-at-trx-commit=0', '--innodb-flush-log-at-timeout=30', '--innodb-flush-method=nosync', '--innodb-fsync-threshold=1000000000']
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: "1.21"
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install --no-install-recommends -qq -y git-lfs
|
||||||
|
- name: setup user and permissions
|
||||||
|
run: |
|
||||||
|
git config --add safe.directory '*'
|
||||||
|
adduser --quiet --comment forgejo --disabled-password forgejo
|
||||||
|
chown -R forgejo:forgejo .
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make deps-backend'
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make backend'
|
||||||
|
env:
|
||||||
|
TAGS: bindata
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make test-mysql-migration test-mysql'
|
||||||
|
timeout-minutes: 50
|
||||||
|
env:
|
||||||
|
TAGS: bindata
|
||||||
|
USE_REPO_TEST_DIR: 1
|
||||||
|
test-pgsql:
|
||||||
|
runs-on: docker
|
||||||
|
needs: [lint-backend, checks-backend]
|
||||||
|
container:
|
||||||
|
image: 'docker.io/node:20-bookworm'
|
||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: bitnami/minio:2021.3.17
|
||||||
|
env:
|
||||||
|
MINIO_ACCESS_KEY: 123456
|
||||||
|
MINIO_SECRET_KEY: 12345678
|
||||||
|
pgsql:
|
||||||
|
image: 'docker.io/postgres:15'
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: test
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: "1.21"
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install --no-install-recommends -qq -y git-lfs
|
||||||
|
- name: setup user and permissions
|
||||||
|
run: |
|
||||||
|
git config --add safe.directory '*'
|
||||||
|
adduser --quiet --comment forgejo --disabled-password forgejo
|
||||||
|
chown -R forgejo:forgejo .
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make deps-backend'
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make backend'
|
||||||
|
env:
|
||||||
|
TAGS: bindata
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make test-pgsql-migration test-pgsql'
|
||||||
|
timeout-minutes: 50
|
||||||
|
env:
|
||||||
|
TAGS: bindata gogit
|
||||||
|
RACE_ENABLED: true
|
||||||
|
TEST_TAGS: gogit
|
||||||
|
USE_REPO_TEST_DIR: 1
|
||||||
|
test-sqlite:
|
||||||
|
runs-on: docker
|
||||||
|
needs: [lint-backend, checks-backend]
|
||||||
|
container:
|
||||||
|
image: 'docker.io/node:20-bookworm'
|
||||||
|
steps:
|
||||||
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
|
- uses: https://code.forgejo.org/actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: "1.21"
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install --no-install-recommends -qq -y git-lfs
|
||||||
|
- name: setup user and permissions
|
||||||
|
run: |
|
||||||
|
git config --add safe.directory '*'
|
||||||
|
adduser --quiet --comment forgejo --disabled-password forgejo
|
||||||
|
chown -R forgejo:forgejo .
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make deps-backend'
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make backend'
|
||||||
|
env:
|
||||||
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
|
- run: |
|
||||||
|
su forgejo -c 'make test-sqlite-migration test-sqlite'
|
||||||
|
timeout-minutes: 50
|
||||||
|
env:
|
||||||
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
|
RACE_ENABLED: true
|
||||||
|
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
||||||
|
USE_REPO_TEST_DIR: 1
|
29
.github/workflows/cron-licenses.yml
vendored
29
.github/workflows/cron-licenses.yml
vendored
|
@ -1,29 +0,0 @@
|
||||||
name: cron-licenses
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "7 0 * * 1" # every Monday at 00:07 UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cron-licenses:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'go-gitea/gitea'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make generate-license generate-gitignore
|
|
||||||
timeout-minutes: 40
|
|
||||||
- name: push translations to repo
|
|
||||||
uses: appleboy/git-push-action@v0.0.3
|
|
||||||
with:
|
|
||||||
author_email: "teabot@gitea.io"
|
|
||||||
author_name: GiteaBot
|
|
||||||
branch: main
|
|
||||||
commit: true
|
|
||||||
commit_message: "[skip ci] Updated licenses and gitignores"
|
|
||||||
remote: "git@github.com:go-gitea/gitea.git"
|
|
||||||
ssh_key: ${{ secrets.DEPLOY_KEY }}
|
|
22
.github/workflows/cron-lock.yml
vendored
22
.github/workflows/cron-lock.yml
vendored
|
@ -1,22 +0,0 @@
|
||||||
name: cron-lock
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 * * *" # every day at 00:00 UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: lock
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
action:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'go-gitea/gitea'
|
|
||||||
steps:
|
|
||||||
- uses: dessant/lock-threads@v4
|
|
||||||
with:
|
|
||||||
issue-inactive-days: 45
|
|
49
.github/workflows/cron-translations.yml
vendored
49
.github/workflows/cron-translations.yml
vendored
|
@ -1,49 +0,0 @@
|
||||||
name: cron-translations
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "7 0 * * *" # every day at 00:07 UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
crowdin-pull:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'go-gitea/gitea'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: download from crowdin
|
|
||||||
uses: docker://jonasfranz/crowdin
|
|
||||||
env:
|
|
||||||
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
|
||||||
PLUGIN_DOWNLOAD: true
|
|
||||||
PLUGIN_EXPORT_DIR: options/locale/
|
|
||||||
PLUGIN_IGNORE_BRANCH: true
|
|
||||||
PLUGIN_PROJECT_IDENTIFIER: gitea
|
|
||||||
- name: update locales
|
|
||||||
run: ./build/update-locales.sh
|
|
||||||
- name: push translations to repo
|
|
||||||
uses: appleboy/git-push-action@v0.0.3
|
|
||||||
with:
|
|
||||||
author_email: "teabot@gitea.io"
|
|
||||||
author_name: GiteaBot
|
|
||||||
branch: main
|
|
||||||
commit: true
|
|
||||||
commit_message: "[skip ci] Updated translations via Crowdin"
|
|
||||||
remote: "git@github.com:go-gitea/gitea.git"
|
|
||||||
ssh_key: ${{ secrets.DEPLOY_KEY }}
|
|
||||||
crowdin-push:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'go-gitea/gitea'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: push translations to crowdin
|
|
||||||
uses: docker://jonasfranz/crowdin
|
|
||||||
env:
|
|
||||||
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
|
||||||
PLUGIN_UPLOAD: true
|
|
||||||
PLUGIN_EXPORT_DIR: options/locale/
|
|
||||||
PLUGIN_IGNORE_BRANCH: true
|
|
||||||
PLUGIN_PROJECT_IDENTIFIER: gitea
|
|
||||||
PLUGIN_FILES: |
|
|
||||||
locale_en-US.ini: options/locale/locale_en-US.ini
|
|
||||||
PLUGIN_BRANCH: main
|
|
182
.github/workflows/pull-compliance.yml
vendored
182
.github/workflows/pull-compliance.yml
vendored
|
@ -1,182 +0,0 @@
|
||||||
name: compliance
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
files-changed:
|
|
||||||
uses: ./.github/workflows/files-changed.yml
|
|
||||||
|
|
||||||
lint-backend:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make deps-backend deps-tools
|
|
||||||
- run: make lint-backend
|
|
||||||
env:
|
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
|
||||||
|
|
||||||
lint-templates:
|
|
||||||
if: needs.files-changed.outputs.templates == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.11"
|
|
||||||
- run: pip install poetry
|
|
||||||
- run: make deps-py
|
|
||||||
- run: make lint-templates
|
|
||||||
|
|
||||||
lint-yaml:
|
|
||||||
if: needs.files-changed.outputs.yaml == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.11"
|
|
||||||
- run: pip install poetry
|
|
||||||
- run: make deps-py
|
|
||||||
- run: make lint-yaml
|
|
||||||
|
|
||||||
lint-swagger:
|
|
||||||
if: needs.files-changed.outputs.swagger == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
- run: make deps-frontend
|
|
||||||
- run: make lint-swagger
|
|
||||||
|
|
||||||
lint-go-windows:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make deps-backend deps-tools
|
|
||||||
- run: make lint-go-windows lint-go-vet
|
|
||||||
env:
|
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
|
||||||
GOOS: windows
|
|
||||||
GOARCH: amd64
|
|
||||||
|
|
||||||
lint-go-gogit:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make deps-backend deps-tools
|
|
||||||
- run: make lint-go
|
|
||||||
env:
|
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
|
||||||
|
|
||||||
checks-backend:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make deps-backend deps-tools
|
|
||||||
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
if: needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
- run: make deps-frontend
|
|
||||||
- run: make lint-frontend
|
|
||||||
- run: make checks-frontend
|
|
||||||
- run: make test-frontend
|
|
||||||
- run: make frontend
|
|
||||||
|
|
||||||
backend:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
# no frontend build here as backend should be able to build
|
|
||||||
# even without any frontend files
|
|
||||||
- run: make deps-backend
|
|
||||||
- run: go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
|
|
||||||
- name: build-backend-arm64
|
|
||||||
run: make backend # test cross compile
|
|
||||||
env:
|
|
||||||
GOOS: linux
|
|
||||||
GOARCH: arm64
|
|
||||||
TAGS: bindata gogit
|
|
||||||
- name: build-backend-windows
|
|
||||||
run: go build -o gitea_windows
|
|
||||||
env:
|
|
||||||
GOOS: windows
|
|
||||||
GOARCH: amd64
|
|
||||||
TAGS: bindata gogit
|
|
||||||
- name: build-backend-386
|
|
||||||
run: go build -o gitea_linux_386 # test if compatible with 32 bit
|
|
||||||
env:
|
|
||||||
GOOS: linux
|
|
||||||
GOARCH: 386
|
|
||||||
|
|
||||||
docs:
|
|
||||||
if: needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
- run: make deps-frontend
|
|
||||||
- run: make lint-md
|
|
||||||
- run: make docs
|
|
||||||
|
|
||||||
actions:
|
|
||||||
if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make lint-actions
|
|
215
.github/workflows/pull-db-tests.yml
vendored
215
.github/workflows/pull-db-tests.yml
vendored
|
@ -1,215 +0,0 @@
|
||||||
name: db-tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
files-changed:
|
|
||||||
uses: ./.github/workflows/files-changed.yml
|
|
||||||
|
|
||||||
test-pgsql:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
pgsql:
|
|
||||||
image: postgres:12
|
|
||||||
env:
|
|
||||||
POSTGRES_DB: test
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
ports:
|
|
||||||
- "5432:5432"
|
|
||||||
ldap:
|
|
||||||
image: gitea/test-openldap:latest
|
|
||||||
ports:
|
|
||||||
- "389:389"
|
|
||||||
- "636:636"
|
|
||||||
minio:
|
|
||||||
# as github actions doesn't support "entrypoint", we need to use a non-official image
|
|
||||||
# that has a custom entrypoint set to "minio server /data"
|
|
||||||
image: bitnami/minio:2023.8.31
|
|
||||||
env:
|
|
||||||
MINIO_ROOT_USER: 123456
|
|
||||||
MINIO_ROOT_PASSWORD: 12345678
|
|
||||||
ports:
|
|
||||||
- "9000:9000"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 pgsql ldap minio" | sudo tee -a /etc/hosts'
|
|
||||||
- run: make deps-backend
|
|
||||||
- run: make backend
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
- run: make test-pgsql-migration test-pgsql
|
|
||||||
timeout-minutes: 50
|
|
||||||
env:
|
|
||||||
TAGS: bindata gogit
|
|
||||||
RACE_ENABLED: true
|
|
||||||
TEST_TAGS: gogit
|
|
||||||
TEST_LDAP: 1
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
|
|
||||||
test-sqlite:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make deps-backend
|
|
||||||
- run: make backend
|
|
||||||
env:
|
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
|
||||||
- run: make test-sqlite-migration test-sqlite
|
|
||||||
timeout-minutes: 50
|
|
||||||
env:
|
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
|
||||||
RACE_ENABLED: true
|
|
||||||
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
|
|
||||||
test-unit:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
elasticsearch:
|
|
||||||
image: elasticsearch:7.5.0
|
|
||||||
env:
|
|
||||||
discovery.type: single-node
|
|
||||||
ports:
|
|
||||||
- "9200:9200"
|
|
||||||
meilisearch:
|
|
||||||
image: getmeili/meilisearch:v1.2.0
|
|
||||||
env:
|
|
||||||
MEILI_ENV: development # disable auth
|
|
||||||
ports:
|
|
||||||
- "7700:7700"
|
|
||||||
redis:
|
|
||||||
image: redis
|
|
||||||
options: >- # wait until redis has started
|
|
||||||
--health-cmd "redis-cli ping"
|
|
||||||
--health-interval 5s
|
|
||||||
--health-timeout 3s
|
|
||||||
--health-retries 10
|
|
||||||
ports:
|
|
||||||
- 6379:6379
|
|
||||||
minio:
|
|
||||||
image: bitnami/minio:2021.3.17
|
|
||||||
env:
|
|
||||||
MINIO_ACCESS_KEY: 123456
|
|
||||||
MINIO_SECRET_KEY: 12345678
|
|
||||||
ports:
|
|
||||||
- "9000:9000"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts'
|
|
||||||
- run: make deps-backend
|
|
||||||
- run: make backend
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
- name: unit-tests
|
|
||||||
run: make unit-test-coverage test-check
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
RACE_ENABLED: true
|
|
||||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
|
||||||
- name: unit-tests-gogit
|
|
||||||
run: make unit-test-coverage test-check
|
|
||||||
env:
|
|
||||||
TAGS: bindata gogit
|
|
||||||
RACE_ENABLED: true
|
|
||||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
|
||||||
|
|
||||||
test-mysql:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
mysql:
|
|
||||||
image: mysql:8.0
|
|
||||||
env:
|
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
|
||||||
MYSQL_DATABASE: testgitea
|
|
||||||
ports:
|
|
||||||
- "3306:3306"
|
|
||||||
elasticsearch:
|
|
||||||
image: elasticsearch:7.5.0
|
|
||||||
env:
|
|
||||||
discovery.type: single-node
|
|
||||||
ports:
|
|
||||||
- "9200:9200"
|
|
||||||
smtpimap:
|
|
||||||
image: tabascoterrier/docker-imap-devel:latest
|
|
||||||
ports:
|
|
||||||
- "25:25"
|
|
||||||
- "143:143"
|
|
||||||
- "587:587"
|
|
||||||
- "993:993"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts'
|
|
||||||
- run: make deps-backend
|
|
||||||
- run: make backend
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
- name: run tests
|
|
||||||
run: make test-mysql-migration integration-test-coverage
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
RACE_ENABLED: true
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
||||||
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
|
||||||
|
|
||||||
test-mssql:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
mssql:
|
|
||||||
image: mcr.microsoft.com/mssql/server:2017-latest
|
|
||||||
env:
|
|
||||||
ACCEPT_EULA: Y
|
|
||||||
MSSQL_PID: Standard
|
|
||||||
SA_PASSWORD: MwantsaSecurePassword1
|
|
||||||
ports:
|
|
||||||
- "1433:1433"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql" | sudo tee -a /etc/hosts'
|
|
||||||
- run: make deps-backend
|
|
||||||
- run: make backend
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
- run: make test-mssql-migration test-mssql
|
|
||||||
timeout-minutes: 50
|
|
||||||
env:
|
|
||||||
TAGS: bindata
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
35
.github/workflows/pull-docker-dryrun.yml
vendored
35
.github/workflows/pull-docker-dryrun.yml
vendored
|
@ -1,35 +0,0 @@
|
||||||
name: docker-dryrun
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
files-changed:
|
|
||||||
uses: ./.github/workflows/files-changed.yml
|
|
||||||
|
|
||||||
regular:
|
|
||||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
push: false
|
|
||||||
tags: gitea/gitea:linux-amd64
|
|
||||||
|
|
||||||
rootless:
|
|
||||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
push: false
|
|
||||||
file: Dockerfile.rootless
|
|
||||||
tags: gitea/gitea:linux-amd64
|
|
32
.github/workflows/pull-e2e-tests.yml
vendored
32
.github/workflows/pull-e2e-tests.yml
vendored
|
@ -1,32 +0,0 @@
|
||||||
name: e2e-tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
files-changed:
|
|
||||||
uses: ./.github/workflows/files-changed.yml
|
|
||||||
|
|
||||||
test-e2e:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20
|
|
||||||
- run: make deps-frontend frontend deps-backend
|
|
||||||
- run: npx playwright install --with-deps
|
|
||||||
- run: make test-e2e-sqlite
|
|
||||||
timeout-minutes: 40
|
|
||||||
env:
|
|
||||||
USE_REPO_TEST_DIR: 1
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
# Emacs
|
||||||
|
*~
|
||||||
|
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
|
|
45
Dockerfile
45
Dockerfile
|
@ -1,5 +1,6 @@
|
||||||
# Build stage
|
FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx
|
||||||
FROM docker.io/library/golang:1.21-alpine3.18 AS build-env
|
|
||||||
|
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21-alpine3.18 as build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
@ -9,24 +10,33 @@ ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
ENV TAGS "bindata timetzdata $TAGS"
|
ENV TAGS "bindata timetzdata $TAGS"
|
||||||
ARG CGO_EXTRA_CFLAGS
|
ARG CGO_EXTRA_CFLAGS
|
||||||
|
|
||||||
# Build deps
|
#
|
||||||
RUN apk --no-cache add \
|
# Transparently cross compile for the target platform
|
||||||
build-base \
|
#
|
||||||
git \
|
COPY --from=xx / /
|
||||||
nodejs \
|
ARG TARGETPLATFORM
|
||||||
npm \
|
RUN apk --no-cache add clang lld
|
||||||
&& rm -rf /var/cache/apk/*
|
RUN xx-apk --no-cache add gcc musl-dev
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
RUN xx-go --wrap
|
||||||
|
#
|
||||||
|
# for go generate and binfmt to find
|
||||||
|
# without it the generate phase will fail with
|
||||||
|
# #19 25.04 modules/public/public_bindata.go:8: running "go": exit status 1
|
||||||
|
# #19 25.39 aarch64-binfmt-P: Could not open '/lib/ld-musl-aarch64.so.1': No such file or directory
|
||||||
|
# why exactly is it needed? where is binfmt involved?
|
||||||
|
#
|
||||||
|
RUN cp /*-alpine-linux-musl*/lib/ld-musl-*.so.1 /lib || true
|
||||||
|
|
||||||
|
RUN apk --no-cache add build-base git nodejs npm
|
||||||
|
|
||||||
# Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||||
|
|
||||||
# Checkout version if set
|
RUN make clean-all
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN make frontend
|
||||||
&& make clean-all build
|
RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini
|
||||||
|
RUN make go-check generate-backend static-executable && xx-verify gitea
|
||||||
# Begin env-to-ini build
|
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|
||||||
# Copy local files
|
# Copy local files
|
||||||
COPY docker/root /tmp/local
|
COPY docker/root /tmp/local
|
||||||
|
@ -42,7 +52,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
||||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.18
|
FROM docker.io/library/alpine:3.18
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
LABEL maintainer="contact@forgejo.org"
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
|
|
||||||
|
@ -81,6 +91,7 @@ ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||||
|
|
||||||
COPY --from=build-env /tmp/local /
|
COPY --from=build-env /tmp/local /
|
||||||
|
RUN cd /usr/local/bin ; ln -s gitea forgejo
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# Build stage
|
FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx
|
||||||
FROM docker.io/library/golang:1.21-alpine3.18 AS build-env
|
|
||||||
|
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21-alpine3.18 as build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
@ -9,24 +10,33 @@ ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
ENV TAGS "bindata timetzdata $TAGS"
|
ENV TAGS "bindata timetzdata $TAGS"
|
||||||
ARG CGO_EXTRA_CFLAGS
|
ARG CGO_EXTRA_CFLAGS
|
||||||
|
|
||||||
#Build deps
|
#
|
||||||
RUN apk --no-cache add \
|
# Transparently cross compile for the target platform
|
||||||
build-base \
|
#
|
||||||
git \
|
COPY --from=xx / /
|
||||||
nodejs \
|
ARG TARGETPLATFORM
|
||||||
npm \
|
RUN apk --no-cache add clang lld
|
||||||
&& rm -rf /var/cache/apk/*
|
RUN xx-apk --no-cache add gcc musl-dev
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
RUN xx-go --wrap
|
||||||
|
#
|
||||||
|
# for go generate and binfmt to find
|
||||||
|
# without it the generate phase will fail with
|
||||||
|
# #19 25.04 modules/public/public_bindata.go:8: running "go": exit status 1
|
||||||
|
# #19 25.39 aarch64-binfmt-P: Could not open '/lib/ld-musl-aarch64.so.1': No such file or directory
|
||||||
|
# why exactly is it needed? where is binfmt involved?
|
||||||
|
#
|
||||||
|
RUN cp /*-alpine-linux-musl*/lib/ld-musl-*.so.1 /lib || true
|
||||||
|
|
||||||
|
RUN apk --no-cache add build-base git nodejs npm
|
||||||
|
|
||||||
# Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||||
|
|
||||||
# Checkout version if set
|
RUN make clean-all
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN make frontend
|
||||||
&& make clean-all build
|
RUN go build contrib/environment-to-ini/environment-to-ini.go && xx-verify environment-to-ini
|
||||||
|
RUN make go-check generate-backend static-executable && xx-verify gitea
|
||||||
# Begin env-to-ini build
|
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|
||||||
# Copy local files
|
# Copy local files
|
||||||
COPY docker/rootless /tmp/local
|
COPY docker/rootless /tmp/local
|
||||||
|
@ -40,7 +50,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
||||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.18
|
FROM docker.io/library/alpine:3.18
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
LABEL maintainer="contact@forgejo.org"
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
EXPOSE 2222 3000
|
||||||
|
|
||||||
|
@ -69,18 +79,19 @@ RUN mkdir -p /var/lib/gitea /etc/gitea
|
||||||
RUN chown git:git /var/lib/gitea /etc/gitea
|
RUN chown git:git /var/lib/gitea /etc/gitea
|
||||||
|
|
||||||
COPY --from=build-env /tmp/local /
|
COPY --from=build-env /tmp/local /
|
||||||
|
RUN cd /usr/local/bin ; ln -s gitea forgejo
|
||||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
|
||||||
# git:git
|
#git:git
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
ENV GITEA_WORK_DIR /var/lib/gitea
|
ENV GITEA_WORK_DIR /var/lib/gitea
|
||||||
ENV GITEA_CUSTOM /var/lib/gitea/custom
|
ENV GITEA_CUSTOM /var/lib/gitea/custom
|
||||||
ENV GITEA_TEMP /tmp/gitea
|
ENV GITEA_TEMP /tmp/gitea
|
||||||
ENV TMPDIR /tmp/gitea
|
ENV TMPDIR /tmp/gitea
|
||||||
|
|
||||||
# TODO add to docs the ability to define the ini to load (useful to test and revert a config)
|
#TODO add to docs the ability to define the ini to load (useful to test and revert a config)
|
||||||
ENV GITEA_APP_INI /etc/gitea/app.ini
|
ENV GITEA_APP_INI /etc/gitea/app.ini
|
||||||
ENV HOME "/var/lib/gitea/git"
|
ENV HOME "/var/lib/gitea/git"
|
||||||
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
||||||
|
@ -88,3 +99,4 @@ WORKDIR /var/lib/gitea
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"]
|
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"]
|
||||||
CMD []
|
CMD []
|
||||||
|
|
||||||
|
|
47
Makefile
47
Makefile
|
@ -83,31 +83,14 @@ endif
|
||||||
STORED_VERSION_FILE := VERSION
|
STORED_VERSION_FILE := VERSION
|
||||||
HUGO_VERSION ?= 0.111.3
|
HUGO_VERSION ?= 0.111.3
|
||||||
|
|
||||||
GITHUB_REF_TYPE ?= branch
|
|
||||||
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
|
||||||
|
|
||||||
ifneq ($(GITHUB_REF_TYPE),branch)
|
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
||||||
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
|
ifneq ($(STORED_VERSION),)
|
||||||
GITEA_VERSION ?= $(VERSION)
|
GITEA_VERSION ?= $(STORED_VERSION)
|
||||||
else
|
else
|
||||||
ifneq ($(GITHUB_REF_NAME),)
|
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||||
VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))
|
|
||||||
else
|
|
||||||
VERSION ?= main
|
|
||||||
endif
|
|
||||||
|
|
||||||
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
|
||||||
ifneq ($(STORED_VERSION),)
|
|
||||||
GITEA_VERSION ?= $(STORED_VERSION)
|
|
||||||
else
|
|
||||||
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
# if version = "main" then update version to "nightly"
|
|
||||||
ifeq ($(VERSION),main)
|
|
||||||
VERSION := main-nightly
|
|
||||||
endif
|
endif
|
||||||
|
VERSION = ${GITEA_VERSION}
|
||||||
|
|
||||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||||
|
|
||||||
|
@ -413,8 +396,7 @@ lint-go-windows:
|
||||||
.PHONY: lint-go-vet
|
.PHONY: lint-go-vet
|
||||||
lint-go-vet:
|
lint-go-vet:
|
||||||
@echo "Running go vet..."
|
@echo "Running go vet..."
|
||||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
@$(GO) vet $(GO_PACKAGES)
|
||||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: lint-editorconfig
|
.PHONY: lint-editorconfig
|
||||||
lint-editorconfig:
|
lint-editorconfig:
|
||||||
|
@ -788,8 +770,14 @@ security-check:
|
||||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
|
static-executable: $(GO_SOURCES) $(TAGS_PREREQ)
|
||||||
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -o $(EXECUTABLE)
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-docs release-check
|
release: frontend generate release-linux release-copy release-compress vendor release-sources release-check
|
||||||
|
|
||||||
|
# just the sources, with all assets builtin and frontend resources generated
|
||||||
|
sources-tarbal: frontend generate vendor release-sources release-check
|
||||||
|
|
||||||
$(DIST_DIRS):
|
$(DIST_DIRS):
|
||||||
mkdir -p $(DIST_DIRS)
|
mkdir -p $(DIST_DIRS)
|
||||||
|
@ -803,7 +791,10 @@ endif
|
||||||
|
|
||||||
.PHONY: release-linux
|
.PHONY: release-linux
|
||||||
release-linux: | $(DIST_DIRS)
|
release-linux: | $(DIST_DIRS)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out forgejo-$(VERSION) .
|
||||||
|
ifeq ($(CI),true)
|
||||||
|
cp /build/* $(DIST)/binaries
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: release-darwin
|
.PHONY: release-darwin
|
||||||
release-darwin: | $(DIST_DIRS)
|
release-darwin: | $(DIST_DIRS)
|
||||||
|
@ -831,8 +822,8 @@ release-sources: | $(DIST_DIRS)
|
||||||
# bsdtar needs a ^ to prevent matching subdirectories
|
# bsdtar needs a ^ to prevent matching subdirectories
|
||||||
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
|
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
|
||||||
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
|
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
|
||||||
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./gitea-src-$(VERSION)/'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
|
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./forgejo-src-$(VERSION)/'" || echo "--transform 's|^./|forgejo-src-$(VERSION)/|'"))
|
||||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/forgejo-src-$(VERSION).tar.gz .
|
||||||
rm -f $(STORED_VERSION_FILE)
|
rm -f $(STORED_VERSION_FILE)
|
||||||
|
|
||||||
.PHONY: release-docs
|
.PHONY: release-docs
|
||||||
|
|
243
cmd/forgejo/actions.go
Normal file
243
cmd/forgejo/actions.go
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
// Copyright The Forgejo Authors.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package forgejo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
private_routers "code.gitea.io/gitea/routers/private"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CmdActions(ctx context.Context) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "actions",
|
||||||
|
Usage: "Commands for managing Forgejo Actions",
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
SubcmdActionsGenerateRunnerToken(ctx),
|
||||||
|
SubcmdActionsGenerateRunnerSecret(ctx),
|
||||||
|
SubcmdActionsRegister(ctx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubcmdActionsGenerateRunnerToken(ctx context.Context) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "generate-runner-token",
|
||||||
|
Usage: "Generate a new token for a runner to use to register with the server",
|
||||||
|
Action: prepareWorkPathAndCustomConf(ctx, func(cliCtx *cli.Context) error { return RunGenerateActionsRunnerToken(ctx, cliCtx) }),
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "scope",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "{owner}[/{repo}] - leave empty for a global runner",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubcmdActionsGenerateRunnerSecret(ctx context.Context) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "generate-secret",
|
||||||
|
Usage: "Generate a secret suitable for input to the register subcommand",
|
||||||
|
Action: func(cliCtx *cli.Context) error { return RunGenerateSecret(ctx, cliCtx) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubcmdActionsRegister(ctx context.Context) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "register",
|
||||||
|
Usage: "Idempotent registration of a runner using a shared secret",
|
||||||
|
Action: prepareWorkPathAndCustomConf(ctx, func(cliCtx *cli.Context) error { return RunRegister(ctx, cliCtx) }),
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "secret",
|
||||||
|
Usage: "the secret the runner will use to connect as a 40 character hexadecimal string",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "secret-stdin",
|
||||||
|
Usage: "the secret the runner will use to connect as a 40 character hexadecimal string, read from stdin",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "secret-file",
|
||||||
|
Usage: "path to the file containing the secret the runner will use to connect as a 40 character hexadecimal string",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "scope",
|
||||||
|
Aliases: []string{"s"},
|
||||||
|
Value: "",
|
||||||
|
Usage: "{owner}[/{repo}] - leave empty for a global runner",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "labels",
|
||||||
|
Value: "",
|
||||||
|
Usage: "comma separated list of labels supported by the runner (e.g. docker,ubuntu-latest,self-hosted) (not required since v1.21)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Value: "runner",
|
||||||
|
Usage: "name of the runner (default runner)",
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "version",
|
||||||
|
Value: "",
|
||||||
|
Usage: "version of the runner (not required since v1.21)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSecret(ctx context.Context, cliCtx *cli.Context) (string, error) {
|
||||||
|
if cliCtx.IsSet("secret") {
|
||||||
|
return cliCtx.String("secret"), nil
|
||||||
|
}
|
||||||
|
if cliCtx.IsSet("secret-stdin") {
|
||||||
|
buf, err := io.ReadAll(ContextGetStdin(ctx))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
if cliCtx.IsSet("secret-file") {
|
||||||
|
path := cliCtx.String("secret-file")
|
||||||
|
buf, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("at least one of the --secret, --secret-stdin, --secret-file options is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSecret(secret string) error {
|
||||||
|
secretLen := len(secret)
|
||||||
|
if secretLen != 40 {
|
||||||
|
return fmt.Errorf("the secret must be exactly 40 characters long, not %d: generate-secret can provide a secret matching the requirements", secretLen)
|
||||||
|
}
|
||||||
|
if _, err := hex.DecodeString(secret); err != nil {
|
||||||
|
return fmt.Errorf("the secret must be an hexadecimal string: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunRegister(ctx context.Context, cliCtx *cli.Context) error {
|
||||||
|
if !ContextGetNoInit(ctx) {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = installSignals(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setting.MustInstalled()
|
||||||
|
|
||||||
|
secret, err := readSecret(ctx, cliCtx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := validateSecret(secret); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scope := cliCtx.String("scope")
|
||||||
|
labels := cliCtx.String("labels")
|
||||||
|
name := cliCtx.String("name")
|
||||||
|
version := cliCtx.String("version")
|
||||||
|
|
||||||
|
//
|
||||||
|
// There are two kinds of tokens
|
||||||
|
//
|
||||||
|
// - "registration token" only used when a runner interacts to
|
||||||
|
// register
|
||||||
|
//
|
||||||
|
// - "token" obtained after a successful registration and stored by
|
||||||
|
// the runner to authenticate
|
||||||
|
//
|
||||||
|
// The register subcommand does not need a "registration token", it
|
||||||
|
// needs a "token". Using the same name is confusing and secret is
|
||||||
|
// preferred for this reason in the cli.
|
||||||
|
//
|
||||||
|
// The ActionsRunnerRegister argument is token to be consistent with
|
||||||
|
// the internal naming. It is still confusing to the developer but
|
||||||
|
// not to the user.
|
||||||
|
//
|
||||||
|
owner, repo, err := private_routers.ParseScope(ctx, scope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
runner, err := actions_model.RegisterRunner(ctx, owner, repo, secret, strings.Split(labels, ","), name, version)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while registering runner: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprintf(ContextGetStdout(ctx), "%s", runner.UUID); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunGenerateSecret(ctx context.Context, cliCtx *cli.Context) error {
|
||||||
|
runner := actions_model.ActionRunner{}
|
||||||
|
if err := runner.GenerateToken(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := fmt.Fprintf(ContextGetStdout(ctx), "%s", runner.Token); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunGenerateActionsRunnerToken(ctx context.Context, cliCtx *cli.Context) error {
|
||||||
|
if !ContextGetNoInit(ctx) {
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = installSignals(ctx)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.MustInstalled()
|
||||||
|
|
||||||
|
scope := cliCtx.String("scope")
|
||||||
|
|
||||||
|
respText, extra := private.GenerateActionsRunnerToken(ctx, scope)
|
||||||
|
if extra.HasError() {
|
||||||
|
return handleCliResponseExtra(ctx, extra)
|
||||||
|
}
|
||||||
|
if _, err := fmt.Fprintf(ContextGetStdout(ctx), "%s", respText); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareWorkPathAndCustomConf(ctx context.Context, action cli.ActionFunc) func(cliCtx *cli.Context) error {
|
||||||
|
return func(cliCtx *cli.Context) error {
|
||||||
|
if !ContextGetNoInit(ctx) {
|
||||||
|
var args setting.ArgWorkPathAndCustomConf
|
||||||
|
// from children to parent, check the global flags
|
||||||
|
for _, curCtx := range cliCtx.Lineage() {
|
||||||
|
if curCtx.IsSet("work-path") && args.WorkPath == "" {
|
||||||
|
args.WorkPath = curCtx.String("work-path")
|
||||||
|
}
|
||||||
|
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
|
||||||
|
args.CustomPath = curCtx.String("custom-path")
|
||||||
|
}
|
||||||
|
if curCtx.IsSet("config") && args.CustomConf == "" {
|
||||||
|
args.CustomConf = curCtx.String("config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
||||||
|
}
|
||||||
|
return action(cliCtx)
|
||||||
|
}
|
||||||
|
}
|
147
cmd/forgejo/forgejo.go
Normal file
147
cmd/forgejo/forgejo.go
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// Copyright The Forgejo Authors.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package forgejo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type key int
|
||||||
|
|
||||||
|
const (
|
||||||
|
noInitKey key = iota + 1
|
||||||
|
noExitKey
|
||||||
|
stdoutKey
|
||||||
|
stderrKey
|
||||||
|
stdinKey
|
||||||
|
)
|
||||||
|
|
||||||
|
func CmdForgejo(ctx context.Context) *cli.Command {
|
||||||
|
return &cli.Command{
|
||||||
|
Name: "forgejo-cli",
|
||||||
|
Usage: "Forgejo CLI",
|
||||||
|
Flags: []cli.Flag{},
|
||||||
|
Subcommands: []*cli.Command{
|
||||||
|
CmdActions(ctx),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextSetNoInit(ctx context.Context, value bool) context.Context {
|
||||||
|
return context.WithValue(ctx, noInitKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextGetNoInit(ctx context.Context) bool {
|
||||||
|
value, ok := ctx.Value(noInitKey).(bool)
|
||||||
|
return ok && value
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextSetNoExit(ctx context.Context, value bool) context.Context {
|
||||||
|
return context.WithValue(ctx, noExitKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextGetNoExit(ctx context.Context) bool {
|
||||||
|
value, ok := ctx.Value(noExitKey).(bool)
|
||||||
|
return ok && value
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextSetStderr(ctx context.Context, value io.Writer) context.Context {
|
||||||
|
return context.WithValue(ctx, stderrKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextGetStderr(ctx context.Context) io.Writer {
|
||||||
|
value, ok := ctx.Value(stderrKey).(io.Writer)
|
||||||
|
if !ok {
|
||||||
|
return os.Stderr
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextSetStdout(ctx context.Context, value io.Writer) context.Context {
|
||||||
|
return context.WithValue(ctx, stdoutKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextGetStdout(ctx context.Context) io.Writer {
|
||||||
|
value, ok := ctx.Value(stderrKey).(io.Writer)
|
||||||
|
if !ok {
|
||||||
|
return os.Stdout
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextSetStdin(ctx context.Context, value io.Reader) context.Context {
|
||||||
|
return context.WithValue(ctx, stdinKey, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextGetStdin(ctx context.Context) io.Reader {
|
||||||
|
value, ok := ctx.Value(stdinKey).(io.Reader)
|
||||||
|
if !ok {
|
||||||
|
return os.Stdin
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from ../cmd.go
|
||||||
|
func initDB(ctx context.Context) error {
|
||||||
|
setting.MustInstalled()
|
||||||
|
setting.LoadDBSetting()
|
||||||
|
setting.InitSQLLoggersForCli(log.INFO)
|
||||||
|
|
||||||
|
if setting.Database.Type == "" {
|
||||||
|
log.Fatal(`Database settings are missing from the configuration file: %q.
|
||||||
|
Ensure you are running in the correct environment or set the correct configuration file with -c.
|
||||||
|
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
|
||||||
|
}
|
||||||
|
if err := db.InitEngine(ctx); err != nil {
|
||||||
|
return fmt.Errorf("unable to initialize the database using the configuration in %q. Error: %w", setting.CustomConf, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from ../cmd.go
|
||||||
|
func installSignals(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
go func() {
|
||||||
|
// install notify
|
||||||
|
signalChannel := make(chan os.Signal, 1)
|
||||||
|
|
||||||
|
signal.Notify(
|
||||||
|
signalChannel,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
)
|
||||||
|
select {
|
||||||
|
case <-signalChannel:
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
signal.Reset()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ctx, cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleCliResponseExtra(ctx context.Context, extra private.ResponseExtra) error {
|
||||||
|
if false && extra.UserMsg != "" {
|
||||||
|
if _, err := fmt.Fprintf(ContextGetStdout(ctx), "%s", extra.UserMsg); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ContextGetNoExit(ctx) {
|
||||||
|
return extra.Error
|
||||||
|
}
|
||||||
|
return cli.Exit(extra.Error, 1)
|
||||||
|
}
|
36
cmd/main.go
36
cmd/main.go
|
@ -4,10 +4,13 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/cmd/forgejo"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
@ -119,6 +122,36 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMainApp(version, versionExtra string) *cli.App {
|
func NewMainApp(version, versionExtra string) *cli.App {
|
||||||
|
path, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
executable := filepath.Base(path)
|
||||||
|
|
||||||
|
var subCmdsStandalone []*cli.Command = make([]*cli.Command, 0, 10)
|
||||||
|
var subCmdWithConfig []*cli.Command = make([]*cli.Command, 0, 10)
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the executable is forgejo-cli, provide a Forgejo specific CLI
|
||||||
|
// that is NOT compatible with Gitea.
|
||||||
|
//
|
||||||
|
if executable == "forgejo-cli" {
|
||||||
|
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdActions(context.Background()))
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// Otherwise provide a Gitea compatible CLI which includes Forgejo
|
||||||
|
// specific additions under the forgejo-cli subcommand. It allows
|
||||||
|
// admins to migration from Gitea to Forgejo by replacing the gitea
|
||||||
|
// binary and rename it to forgejo if they want.
|
||||||
|
//
|
||||||
|
subCmdsStandalone = append(subCmdsStandalone, forgejo.CmdForgejo(context.Background()))
|
||||||
|
subCmdWithConfig = append(subCmdWithConfig, CmdActions)
|
||||||
|
}
|
||||||
|
|
||||||
|
return innerNewMainApp(version, versionExtra, subCmdsStandalone, subCmdWithConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func innerNewMainApp(version, versionExtra string, subCmdsStandaloneArgs, subCmdWithConfigArgs []*cli.Command) *cli.App {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "Gitea"
|
app.Name = "Gitea"
|
||||||
app.Usage = "A painless self-hosted Git service"
|
app.Usage = "A painless self-hosted Git service"
|
||||||
|
@ -141,13 +174,13 @@ func NewMainApp(version, versionExtra string) *cli.App {
|
||||||
CmdMigrateStorage,
|
CmdMigrateStorage,
|
||||||
CmdDumpRepository,
|
CmdDumpRepository,
|
||||||
CmdRestoreRepository,
|
CmdRestoreRepository,
|
||||||
CmdActions,
|
|
||||||
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
|
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdConvert := util.ToPointer(*cmdDoctorConvert)
|
cmdConvert := util.ToPointer(*cmdDoctorConvert)
|
||||||
cmdConvert.Hidden = true // still support the legacy "./gitea doctor" by the hidden sub-command, remove it in next release
|
cmdConvert.Hidden = true // still support the legacy "./gitea doctor" by the hidden sub-command, remove it in next release
|
||||||
subCmdWithConfig = append(subCmdWithConfig, cmdConvert)
|
subCmdWithConfig = append(subCmdWithConfig, cmdConvert)
|
||||||
|
subCmdWithConfig = append(subCmdWithConfig, subCmdWithConfigArgs...)
|
||||||
|
|
||||||
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
||||||
subCmdStandalone := []*cli.Command{
|
subCmdStandalone := []*cli.Command{
|
||||||
|
@ -155,6 +188,7 @@ func NewMainApp(version, versionExtra string) *cli.App {
|
||||||
CmdGenerate,
|
CmdGenerate,
|
||||||
CmdDocs,
|
CmdDocs,
|
||||||
}
|
}
|
||||||
|
subCmdStandalone = append(subCmdStandalone, subCmdsStandaloneArgs...)
|
||||||
|
|
||||||
app.DefaultCommand = CmdWeb.Name
|
app.DefaultCommand = CmdWeb.Name
|
||||||
|
|
||||||
|
|
|
@ -2572,9 +2572,8 @@ LEVEL = Info
|
||||||
; [actions]
|
; [actions]
|
||||||
;; Enable/Disable actions capabilities
|
;; Enable/Disable actions capabilities
|
||||||
;ENABLED = true
|
;ENABLED = true
|
||||||
;;
|
;; Default address to get action plugins, e.g. the default value means downloading from "https://code.forgejo.org/actions/checkout" for "uses: actions/checkout@v3"
|
||||||
;; Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance.
|
;DEFAULT_ACTIONS_URL = https://code.forgejo.org
|
||||||
;DEFAULT_ACTIONS_URL = github
|
|
||||||
;; Default artifact retention time in days, default is 90 days
|
;; Default artifact retention time in days, default is 90 days
|
||||||
;ARTIFACT_RETENTION_DAYS = 90
|
;ARTIFACT_RETENTION_DAYS = 90
|
||||||
;; Timeout to stop the task which have running status, but haven't been updated for a long time
|
;; Timeout to stop the task which have running status, but haven't been updated for a long time
|
||||||
|
|
68
models/actions/forgejo.go
Normal file
68
models/actions/forgejo.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
gouuid "github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels []string, name, version string) (*ActionRunner, error) {
|
||||||
|
uuid, err := gouuid.FromBytes([]byte(token[:16]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("gouuid.FromBytes %v", err)
|
||||||
|
}
|
||||||
|
uuidString := uuid.String()
|
||||||
|
|
||||||
|
var runner ActionRunner
|
||||||
|
|
||||||
|
has, err := db.GetEngine(ctx).Where("uuid=?", uuidString).Get(&runner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("GetRunner %v", err)
|
||||||
|
} else if !has {
|
||||||
|
//
|
||||||
|
// The runner does not exist yet, create it
|
||||||
|
//
|
||||||
|
saltBytes, err := util.CryptoRandomBytes(16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("CryptoRandomBytes %v", err)
|
||||||
|
}
|
||||||
|
salt := hex.EncodeToString(saltBytes)
|
||||||
|
|
||||||
|
hash := auth_model.HashToken(token, salt)
|
||||||
|
|
||||||
|
runner = ActionRunner{
|
||||||
|
UUID: uuidString,
|
||||||
|
TokenHash: hash,
|
||||||
|
TokenSalt: salt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := CreateRunner(ctx, &runner); err != nil {
|
||||||
|
return &runner, fmt.Errorf("can't create new runner %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update the existing runner
|
||||||
|
//
|
||||||
|
name, _ = util.SplitStringAtByteN(name, 255)
|
||||||
|
|
||||||
|
runner.Name = name
|
||||||
|
runner.OwnerID = ownerID
|
||||||
|
runner.RepoID = repoID
|
||||||
|
runner.Version = version
|
||||||
|
runner.AgentLabels = labels
|
||||||
|
|
||||||
|
if err := UpdateRunner(ctx, &runner, "name", "owner_id", "repo_id", "version", "agent_labels"); err != nil {
|
||||||
|
return &runner, fmt.Errorf("can't update the runner %+v %w", runner, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &runner, nil
|
||||||
|
}
|
29
models/actions/forgejo_test.go
Normal file
29
models/actions/forgejo_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestActions_RegisterRunner(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
ownerID := int64(0)
|
||||||
|
repoID := int64(0)
|
||||||
|
token := "0123456789012345678901234567890123456789"
|
||||||
|
labels := []string{}
|
||||||
|
name := "runner"
|
||||||
|
version := "v1.2.3"
|
||||||
|
runner, err := RegisterRunner(db.DefaultContext, ownerID, repoID, token, labels, name, version)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, name, runner.Name)
|
||||||
|
|
||||||
|
assert.EqualValues(t, 1, subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))), "the token cannot be verified with the same method as routers/api/actions/runner/interceptor.go as of 8228751c55d6a4263f0fec2932ca16181c09c97d")
|
||||||
|
}
|
|
@ -40,11 +40,14 @@ func IsWorkflow(path string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows")
|
return strings.HasPrefix(path, ".forgejo/workflows") || strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
||||||
tree, err := commit.SubTree(".gitea/workflows")
|
tree, err := commit.SubTree(".forgejo/workflows")
|
||||||
|
if _, ok := err.(git.ErrNotExist); ok {
|
||||||
|
tree, err = commit.SubTree(".gitea/workflows")
|
||||||
|
}
|
||||||
if _, ok := err.(git.ErrNotExist); ok {
|
if _, ok := err.(git.ErrNotExist); ok {
|
||||||
tree, err = commit.SubTree(".github/workflows")
|
tree, err = commit.SubTree(".github/workflows")
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestElasticsearchIndexer(t *testing.T) {
|
func TestElasticsearchIndexer(t *testing.T) {
|
||||||
|
t.Skip("elasticsearch not found in Forgejo test yet")
|
||||||
// The elasticsearch instance started by pull-db-tests.yml > test-unit > services > elasticsearch
|
// The elasticsearch instance started by pull-db-tests.yml > test-unit > services > elasticsearch
|
||||||
url := "http://elastic:changeme@elasticsearch:9200"
|
url := "http://elastic:changeme@elasticsearch:9200"
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMeilisearchIndexer(t *testing.T) {
|
func TestMeilisearchIndexer(t *testing.T) {
|
||||||
|
t.Skip("meilisearch not found in Forgejo test yet")
|
||||||
// The meilisearch instance started by pull-db-tests.yml > test-unit > services > meilisearch
|
// The meilisearch instance started by pull-db-tests.yml > test-unit > services > meilisearch
|
||||||
url := "http://meilisearch:7700"
|
url := "http://meilisearch:7700"
|
||||||
key := "" // auth has been disabled in test environment
|
key := "" // auth has been disabled in test environment
|
||||||
|
|
32
modules/private/forgejo_actions.go
Normal file
32
modules/private/forgejo_actions.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package private
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ActionsRunnerRegisterRequest struct {
|
||||||
|
Token string
|
||||||
|
Scope string
|
||||||
|
Labels []string
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ActionsRunnerRegister(ctx context.Context, token, scope string, labels []string, name, version string) (string, ResponseExtra) {
|
||||||
|
reqURL := setting.LocalURL + "api/internal/actions/register"
|
||||||
|
|
||||||
|
req := newInternalRequest(ctx, reqURL, "POST", ActionsRunnerRegisterRequest{
|
||||||
|
Token: token,
|
||||||
|
Scope: scope,
|
||||||
|
Labels: labels,
|
||||||
|
Name: name,
|
||||||
|
Version: version,
|
||||||
|
})
|
||||||
|
|
||||||
|
resp, extra := requestJSONResp(req, &responseText{})
|
||||||
|
return resp.Text, extra
|
||||||
|
}
|
|
@ -56,8 +56,8 @@ func TestBaseRedis(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
if !waitRedisReady("redis://127.0.0.1:6379/0", 0) {
|
if !waitRedisReady("redis://127.0.0.1:6379/0", 0) {
|
||||||
redisServer = redisServerCmd(t)
|
redisServer = redisServerCmd(t)
|
||||||
if redisServer == nil && os.Getenv("CI") == "" {
|
if true {
|
||||||
t.Skip("redis-server not found")
|
t.Skip("redis-server not found in Forgejo test yet")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.NoError(t, redisServer.Start())
|
assert.NoError(t, redisServer.Start())
|
||||||
|
|
|
@ -7,8 +7,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Actions settings
|
// Actions settings
|
||||||
|
@ -24,7 +22,7 @@ var (
|
||||||
AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"`
|
AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"`
|
||||||
}{
|
}{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
DefaultActionsURL: defaultActionsURLGitHub,
|
DefaultActionsURL: defaultActionsURLForgejo,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,18 +35,14 @@ func (url defaultActionsURL) URL() string {
|
||||||
case defaultActionsURLSelf:
|
case defaultActionsURLSelf:
|
||||||
return strings.TrimSuffix(AppURL, "/")
|
return strings.TrimSuffix(AppURL, "/")
|
||||||
default:
|
default:
|
||||||
// This should never happen, but just in case, use GitHub as fallback
|
return string(url)
|
||||||
return "https://github.com"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultActionsURLGitHub = "github" // https://github.com
|
defaultActionsURLForgejo = "https://code.forgejo.org"
|
||||||
defaultActionsURLSelf = "self" // the root URL of the self-hosted Gitea instance
|
defaultActionsURLGitHub = "github" // https://github.com
|
||||||
// DefaultActionsURL only supports GitHub and the self-hosted Gitea.
|
defaultActionsURLSelf = "self" // the root URL of the self-hosted instance
|
||||||
// It's intentionally not supported more, so please be cautious before adding more like "gitea" or "gitlab".
|
|
||||||
// If you get some trouble with `uses: username/action_name@version` in your workflow,
|
|
||||||
// please consider to use `uses: https://the_url_you_want_to_use/username/action_name@version` instead.
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadActionsFrom(rootCfg ConfigProvider) error {
|
func loadActionsFrom(rootCfg ConfigProvider) error {
|
||||||
|
@ -58,19 +52,6 @@ func loadActionsFrom(rootCfg ConfigProvider) error {
|
||||||
return fmt.Errorf("failed to map Actions settings: %v", err)
|
return fmt.Errorf("failed to map Actions settings: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if urls := string(Actions.DefaultActionsURL); urls != defaultActionsURLGitHub && urls != defaultActionsURLSelf {
|
|
||||||
url := strings.Split(urls, ",")[0]
|
|
||||||
if strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "http://") {
|
|
||||||
log.Error("[actions] DEFAULT_ACTIONS_URL does not support %q as custom URL any longer, fallback to %q",
|
|
||||||
urls,
|
|
||||||
defaultActionsURLGitHub,
|
|
||||||
)
|
|
||||||
Actions.DefaultActionsURL = defaultActionsURLGitHub
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("unsupported [actions] DEFAULT_ACTIONS_URL: %q", urls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't support to read configuration from [actions]
|
// don't support to read configuration from [actions]
|
||||||
Actions.LogStorage, err = getStorage(rootCfg, "actions_log", "", nil)
|
Actions.LogStorage, err = getStorage(rootCfg, "actions_log", "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -110,7 +110,6 @@ func Test_getDefaultActionsURLForActions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
iniStr string
|
iniStr string
|
||||||
wantErr assert.ErrorAssertionFunc
|
|
||||||
wantURL string
|
wantURL string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -118,8 +117,7 @@ func Test_getDefaultActionsURLForActions(t *testing.T) {
|
||||||
iniStr: `
|
iniStr: `
|
||||||
[actions]
|
[actions]
|
||||||
`,
|
`,
|
||||||
wantErr: assert.NoError,
|
wantURL: "https://code.forgejo.org",
|
||||||
wantURL: "https://github.com",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "github",
|
name: "github",
|
||||||
|
@ -127,7 +125,6 @@ func Test_getDefaultActionsURLForActions(t *testing.T) {
|
||||||
[actions]
|
[actions]
|
||||||
DEFAULT_ACTIONS_URL = github
|
DEFAULT_ACTIONS_URL = github
|
||||||
`,
|
`,
|
||||||
wantErr: assert.NoError,
|
|
||||||
wantURL: "https://github.com",
|
wantURL: "https://github.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -136,35 +133,15 @@ DEFAULT_ACTIONS_URL = github
|
||||||
[actions]
|
[actions]
|
||||||
DEFAULT_ACTIONS_URL = self
|
DEFAULT_ACTIONS_URL = self
|
||||||
`,
|
`,
|
||||||
wantErr: assert.NoError,
|
|
||||||
wantURL: "http://test_get_default_actions_url_for_actions:3000",
|
wantURL: "http://test_get_default_actions_url_for_actions:3000",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "custom url",
|
|
||||||
iniStr: `
|
|
||||||
[actions]
|
|
||||||
DEFAULT_ACTIONS_URL = https://gitea.com
|
|
||||||
`,
|
|
||||||
wantErr: assert.NoError,
|
|
||||||
wantURL: "https://github.com",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "custom urls",
|
name: "custom urls",
|
||||||
iniStr: `
|
iniStr: `
|
||||||
[actions]
|
[actions]
|
||||||
DEFAULT_ACTIONS_URL = https://gitea.com,https://github.com
|
DEFAULT_ACTIONS_URL = https://example.com
|
||||||
`,
|
`,
|
||||||
wantErr: assert.NoError,
|
wantURL: "https://example.com",
|
||||||
wantURL: "https://github.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid",
|
|
||||||
iniStr: `
|
|
||||||
[actions]
|
|
||||||
DEFAULT_ACTIONS_URL = gitea
|
|
||||||
`,
|
|
||||||
wantErr: assert.Error,
|
|
||||||
wantURL: "https://github.com",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +149,7 @@ DEFAULT_ACTIONS_URL = gitea
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cfg, err := NewConfigProviderFromData(tt.iniStr)
|
cfg, err := NewConfigProviderFromData(tt.iniStr)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if !tt.wantErr(t, loadActionsFrom(cfg)) {
|
if !assert.NoError(t, loadActionsFrom(cfg)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.EqualValues(t, tt.wantURL, Actions.DefaultActionsURL.URL())
|
assert.EqualValues(t, tt.wantURL, Actions.DefaultActionsURL.URL())
|
||||||
|
|
|
@ -22,7 +22,7 @@ func TestMinioStorageIterator(t *testing.T) {
|
||||||
}
|
}
|
||||||
testStorageIterator(t, setting.MinioStorageType, &setting.Storage{
|
testStorageIterator(t, setting.MinioStorageType, &setting.Storage{
|
||||||
MinioConfig: setting.MinioStorageConfig{
|
MinioConfig: setting.MinioStorageConfig{
|
||||||
Endpoint: "127.0.0.1:9000",
|
Endpoint: "minio:9000",
|
||||||
AccessKeyID: "123456",
|
AccessKeyID: "123456",
|
||||||
SecretAccessKey: "12345678",
|
SecretAccessKey: "12345678",
|
||||||
Bucket: "gitea",
|
Bucket: "gitea",
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package private
|
package private
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
gocontext "context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -64,7 +65,11 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) {
|
||||||
ctx.PlainText(http.StatusOK, token.Token)
|
ctx.PlainText(http.StatusOK, token.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseScope(ctx *context.PrivateContext, scope string) (ownerID, repoID int64, err error) {
|
func ParseScope(ctx gocontext.Context, scope string) (ownerID, repoID int64, err error) {
|
||||||
|
return parseScope(ctx, scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScope(ctx gocontext.Context, scope string) (ownerID, repoID int64, err error) {
|
||||||
ownerID = 0
|
ownerID = 0
|
||||||
repoID = 0
|
repoID = 0
|
||||||
if scope == "" {
|
if scope == "" {
|
||||||
|
|
209
tests/integration/cmd_forgejo_actions_test.go
Normal file
209
tests/integration/cmd_forgejo_actions_test.go
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
gocontext "context"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CmdForgejo_Actions(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||||
|
token, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "actions", "generate-runner-token"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 40, len(token))
|
||||||
|
|
||||||
|
secret, err := cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "actions", "generate-secret"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 40, len(secret))
|
||||||
|
|
||||||
|
_, err = cmdForgejoCaptureOutput(t, []string{"forgejo", "forgejo-cli", "actions", "register"})
|
||||||
|
assert.ErrorContains(t, err, "at least one of the --secret")
|
||||||
|
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
testName string
|
||||||
|
scope string
|
||||||
|
secret string
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "bad user",
|
||||||
|
scope: "baduser",
|
||||||
|
secret: "0123456789012345678901234567890123456789",
|
||||||
|
errorMessage: "user does not exist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "bad repo",
|
||||||
|
scope: "org25/badrepo",
|
||||||
|
secret: "0123456789012345678901234567890123456789",
|
||||||
|
errorMessage: "repository does not exist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "secret length != 40",
|
||||||
|
scope: "org25",
|
||||||
|
secret: "0123456789",
|
||||||
|
errorMessage: "40 characters long",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "secret is not a hexadecimal string",
|
||||||
|
scope: "org25",
|
||||||
|
secret: "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
|
||||||
|
errorMessage: "must be an hexadecimal string",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(testCase.testName, func(t *testing.T) {
|
||||||
|
cmd := []string{"forgejo", "forgejo-cli", "actions", "register", "--secret", testCase.secret, "--scope", testCase.scope}
|
||||||
|
output, err := cmdForgejoCaptureOutput(t, cmd)
|
||||||
|
assert.ErrorContains(t, err, testCase.errorMessage)
|
||||||
|
assert.EqualValues(t, "", output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
secret = "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
|
||||||
|
expecteduuid := "44444444-4444-4444-4444-444444444444"
|
||||||
|
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
testName string
|
||||||
|
secretOption func() string
|
||||||
|
stdin []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "secret from argument",
|
||||||
|
secretOption: func() string {
|
||||||
|
return "--secret=" + secret
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "secret from stdin",
|
||||||
|
secretOption: func() string {
|
||||||
|
return "--secret-stdin"
|
||||||
|
},
|
||||||
|
stdin: []string{secret},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "secret from file",
|
||||||
|
secretOption: func() string {
|
||||||
|
secretFile := t.TempDir() + "/secret"
|
||||||
|
assert.NoError(t, os.WriteFile(secretFile, []byte(secret), 0o644))
|
||||||
|
return "--secret-file=" + secretFile
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(testCase.testName, func(t *testing.T) {
|
||||||
|
cmd := []string{"forgejo", "forgejo-cli", "actions", "register", testCase.secretOption(), "--scope=org26"}
|
||||||
|
uuid, err := cmdForgejoCaptureOutput(t, cmd, testCase.stdin...)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, expecteduuid, uuid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
secret = "0123456789012345678901234567890123456789"
|
||||||
|
expecteduuid = "30313233-3435-3637-3839-303132333435"
|
||||||
|
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
testName string
|
||||||
|
scope string
|
||||||
|
secret string
|
||||||
|
name string
|
||||||
|
labels string
|
||||||
|
version string
|
||||||
|
uuid string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "org",
|
||||||
|
scope: "org25",
|
||||||
|
secret: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
uuid: "41414141-4141-4141-4141-414141414141",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "user and repo",
|
||||||
|
scope: "user2/repo2",
|
||||||
|
secret: "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
|
||||||
|
uuid: "42424242-4242-4242-4242-424242424242",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "labels",
|
||||||
|
scope: "org25",
|
||||||
|
name: "runnerName",
|
||||||
|
labels: "label1,label2,label3",
|
||||||
|
version: "v1.2.3",
|
||||||
|
secret: "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
|
||||||
|
uuid: "43434343-4343-4343-4343-434343434343",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "insert a runner",
|
||||||
|
scope: "user10/repo6",
|
||||||
|
name: "runnerName",
|
||||||
|
labels: "label1,label2,label3",
|
||||||
|
version: "v1.2.3",
|
||||||
|
secret: secret,
|
||||||
|
uuid: expecteduuid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "update an existing runner",
|
||||||
|
scope: "user5/repo4",
|
||||||
|
name: "runnerNameChanged",
|
||||||
|
labels: "label1,label2,label3,more,label",
|
||||||
|
version: "v1.2.3-suffix",
|
||||||
|
secret: secret,
|
||||||
|
uuid: expecteduuid,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(testCase.testName, func(t *testing.T) {
|
||||||
|
cmd := []string{
|
||||||
|
"forgejo", "forgejo-cli", "actions", "register",
|
||||||
|
"--secret", testCase.secret, "--scope", testCase.scope,
|
||||||
|
}
|
||||||
|
if testCase.name != "" {
|
||||||
|
cmd = append(cmd, "--name", testCase.name)
|
||||||
|
}
|
||||||
|
if testCase.labels != "" {
|
||||||
|
cmd = append(cmd, "--labels", testCase.labels)
|
||||||
|
}
|
||||||
|
if testCase.version != "" {
|
||||||
|
cmd = append(cmd, "--version", testCase.version)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Run twice to verify it is idempotent
|
||||||
|
//
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
uuid, err := cmdForgejoCaptureOutput(t, cmd)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.EqualValues(t, testCase.uuid, uuid) {
|
||||||
|
ownerName, repoName, found := strings.Cut(testCase.scope, "/")
|
||||||
|
action, err := actions_model.GetRunnerByUUID(gocontext.Background(), uuid)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: action.OwnerID})
|
||||||
|
assert.Equal(t, ownerName, user.Name, action.OwnerID)
|
||||||
|
|
||||||
|
if found {
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: action.RepoID})
|
||||||
|
assert.Equal(t, repoName, repo.Name, action.RepoID)
|
||||||
|
}
|
||||||
|
if testCase.name != "" {
|
||||||
|
assert.EqualValues(t, testCase.name, action.Name)
|
||||||
|
}
|
||||||
|
if testCase.labels != "" {
|
||||||
|
labels := strings.Split(testCase.labels, ",")
|
||||||
|
assert.EqualValues(t, labels, action.AgentLabels)
|
||||||
|
}
|
||||||
|
if testCase.version != "" {
|
||||||
|
assert.EqualValues(t, testCase.version, action.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
36
tests/integration/cmd_forgejo_test.go
Normal file
36
tests/integration/cmd_forgejo_test.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/cmd/forgejo"
|
||||||
|
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cmdForgejoCaptureOutput(t *testing.T, args []string, stdin ...string) (string, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Writer = buf
|
||||||
|
app.ErrWriter = buf
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = forgejo.ContextSetNoInit(ctx, true)
|
||||||
|
ctx = forgejo.ContextSetNoExit(ctx, true)
|
||||||
|
ctx = forgejo.ContextSetStdout(ctx, buf)
|
||||||
|
ctx = forgejo.ContextSetStderr(ctx, buf)
|
||||||
|
if len(stdin) > 0 {
|
||||||
|
ctx = forgejo.ContextSetStdin(ctx, strings.NewReader(strings.Join(stdin, "")))
|
||||||
|
}
|
||||||
|
app.Commands = []*cli.Command{
|
||||||
|
forgejo.CmdForgejo(ctx),
|
||||||
|
}
|
||||||
|
err := app.Run(args)
|
||||||
|
|
||||||
|
return buf.String(), err
|
||||||
|
}
|
|
@ -96,6 +96,7 @@ COLORIZE = true
|
||||||
LEVEL = Debug
|
LEVEL = Debug
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
|
PASSWORD_HASH_ALGO = argon2
|
||||||
DISABLE_GIT_HOOKS = false
|
DISABLE_GIT_HOOKS = false
|
||||||
INSTALL_LOCK = true
|
INSTALL_LOCK = true
|
||||||
SECRET_KEY = 9pCviYTWSb
|
SECRET_KEY = 9pCviYTWSb
|
||||||
|
|
|
@ -12,8 +12,6 @@ SSL_MODE = disable
|
||||||
[indexer]
|
[indexer]
|
||||||
REPO_INDEXER_ENABLED = true
|
REPO_INDEXER_ENABLED = true
|
||||||
REPO_INDEXER_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/indexers/repos.bleve
|
REPO_INDEXER_PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/indexers/repos.bleve
|
||||||
ISSUE_INDEXER_TYPE = elasticsearch
|
|
||||||
ISSUE_INDEXER_CONN_STR = http://elastic:changeme@elasticsearch:9200
|
|
||||||
|
|
||||||
[queue.issue_indexer]
|
[queue.issue_indexer]
|
||||||
TYPE = level
|
TYPE = level
|
||||||
|
@ -94,6 +92,7 @@ COLORIZE = true
|
||||||
LEVEL = Debug
|
LEVEL = Debug
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
|
PASSWORD_HASH_ALGO = argon2
|
||||||
DISABLE_GIT_HOOKS = false
|
DISABLE_GIT_HOOKS = false
|
||||||
INSTALL_LOCK = true
|
INSTALL_LOCK = true
|
||||||
SECRET_KEY = 9pCviYTWSb
|
SECRET_KEY = 9pCviYTWSb
|
||||||
|
|
|
@ -97,6 +97,7 @@ COLORIZE = true
|
||||||
LEVEL = Debug
|
LEVEL = Debug
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
|
PASSWORD_HASH_ALGO = argon2
|
||||||
DISABLE_GIT_HOOKS = false
|
DISABLE_GIT_HOOKS = false
|
||||||
INSTALL_LOCK = true
|
INSTALL_LOCK = true
|
||||||
SECRET_KEY = 9pCviYTWSb
|
SECRET_KEY = 9pCviYTWSb
|
||||||
|
|
|
@ -93,6 +93,7 @@ COLORIZE = true
|
||||||
LEVEL = Debug
|
LEVEL = Debug
|
||||||
|
|
||||||
[security]
|
[security]
|
||||||
|
PASSWORD_HASH_ALGO = argon2
|
||||||
DISABLE_GIT_HOOKS = false
|
DISABLE_GIT_HOOKS = false
|
||||||
INSTALL_LOCK = true
|
INSTALL_LOCK = true
|
||||||
SECRET_KEY = 9pCviYTWSb
|
SECRET_KEY = 9pCviYTWSb
|
||||||
|
|
Loading…
Reference in a new issue