Compare commits

...

No commits in common. "main" and "fix-go-revert" have entirely different histories.

6294 changed files with 73475 additions and 625580 deletions

View File

@ -1,98 +0,0 @@
# syntax=docker/dockerfile:1
# =============================================================================
# Stage 1: Base tools installation (rarely changes, excellent caching)
# =============================================================================
FROM mcr.microsoft.com/devcontainers/go:1-1.23 as tools
# Install system packages in single layer for better caching
RUN sudo apt update && sudo apt install -y \
nodejs \
lsb-release \
curl \
gpg \
protobuf-compiler \
git-lfs \
&& sudo apt-get clean \
&& sudo rm -rf /var/lib/apt/lists/*
# Install Go tools (these rarely change), separately to avoid memory issues
RUN export GOMAXPROCS=1 && go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1
RUN export GOMAXPROCS=1 && go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0
ENV PATH="${PATH}:$(go env GOPATH)/bin"
# =============================================================================
# Stage 2: External services installation (moderate caching)
# =============================================================================
FROM tools as services
ARG TARGETARCH
# Install redis
RUN curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg && \
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list && \
sudo apt-get update -y && \
sudo apt-get install redis -y && \
sudo apt-get clean && \
sudo rm -rf /var/lib/apt/lists/*
# Install gcloud and kubectl
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg && \
sudo apt-get update && \
sudo apt-get install -y \
google-cloud-cli \
kubectl \
google-cloud-cli-gke-gcloud-auth-plugin \
&& sudo apt-get clean \
&& sudo rm -rf /var/lib/apt/lists/*
# Install binary tools with architecture support
RUN curl -fsSL https://raw.githubusercontent.com/metalbear-co/mirrord/main/scripts/install.sh | bash
# Install yq with architecture detection
RUN curl -Lo /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.34.1/yq_linux_${TARGETARCH} && \
sudo chmod +x /usr/local/bin/yq
# Configure Go to handle private modules
RUN go env -w GOPRIVATE=$(go env GOPRIVATE),git0.harness.io
# =============================================================================
# Stage 3: Code preparation and build (secure - credentials cleaned in same layer)
# =============================================================================
FROM services as builder
ARG BRANCH
# Arg to track commit hash — cache busts only when this changes
ARG COMMIT_SHA
WORKDIR /root
RUN --mount=type=secret,id=harness_code_secret_harness0,env=HARNESS_CODE_SECRET_HARNESS0 \
--mount=type=secret,id=harness_code_user,env=HARNESS_CODE_USER \
--mount=type=secret,id=github_secret,env=GITHUB_SECRET \
--mount=type=secret,id=github_user,env=GITHUB_USER \
echo $COMMIT_SHA > /commit.txt && \
git config --global credential.helper store && \
echo "https://${HARNESS_CODE_USER}:${HARNESS_CODE_SECRET_HARNESS0}@git0.harness.io" >> ~/.git-credentials && \
echo "https://${GITHUB_USER}:${GITHUB_SECRET}@github.com" >> ~/.git-credentials && \
echo "@harness:registry=https://npm.pkg.github.com" > ~/.npmrc && \
echo "//npm.pkg.github.com/:_authToken=${GITHUB_SECRET}" >> ~/.npmrc && \
echo "always-auth=true" >> ~/.npmrc && \
echo "machine git0.harness.io login git password ${HARNESS_CODE_SECRET_HARNESS0}" >> ~/.netrc && \
git clone -b ${BRANCH} https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness.git && \
cd /root/gitness && \
git lfs install && git lfs pull && \
make init && \
make dep && \
make tools && \
make web-build && \
make build && \
rm -f ~/.git-credentials && git config --global --unset credential.helper && \
sed -i 's|//npm.pkg.github.com/:_authToken=.*|//npm.pkg.github.com/:_authToken=xxx|' ~/.npmrc && \
sed -i '/machine git0\.harness\.io/d' ~/.netrc
WORKDIR /root/gitness

View File

@ -1,19 +0,0 @@
{
"image": "harness0.harness.io/oci/gitspaces-image-registry/gitness-base:main-amd64",
"remoteUser": "root",
"forwardPorts": [
"3000"
],
"customizations": {
"harnessGitspaces": {
"connectors": [
{
"type": "DockerRegistry",
"identifier": "org.gitspacesimageregistry"
}
]
}
},
"postCreateCommand": "sudo chmod +x /root/gitness/.devcontainer/postCreate.sh && /root/gitness/.devcontainer/postCreate.sh",
"postStartCommand": "sudo chmod +x /root/gitness/.devcontainer/postStart.sh && /root/gitness/.devcontainer/postStart.sh"
}

View File

@ -1,2 +0,0 @@
#!/bin/sh

View File

@ -1,5 +0,0 @@
#!/bin/sh
#sudo service redis-server start
redis-server &

View File

@ -1,12 +1,2 @@
*.sqlite
*.sqlite3
web/node_modules
web/dist
release
.idea
coverage.out
*.rsa
*.rsa.pub
# ignore any executables we build
/gitness
*
!release/*

107
.drone.yml Normal file
View File

@ -0,0 +1,107 @@
---
kind: pipeline
type: docker
name: linux-amd64
platform:
arch: amd64
os: linux
steps:
- name: test
image: golang:1.22.7
commands:
- go test -race ./...
- go build -o /dev/null github.com/drone/drone/cmd/drone-server
- go build -o /dev/null -tags "oss nolimit" github.com/drone/drone/cmd/drone-server
- name: build
image: golang:1.22.7
commands:
- sh scripts/build.sh
environment:
GOARCH: amd64
GOOS: linux
- name: publish
image: plugins/docker:18
settings:
auto_tag: true
auto_tag_suffix: linux-amd64
dockerfile: docker/Dockerfile.server.linux.amd64
repo: drone/drone
username:
from_secret: docker_username
password:
from_secret: docker_password
when:
event:
- push
- tag
---
kind: pipeline
type: vm
name: linux-arm64
pool:
use: ubuntu_arm64
platform:
arch: arm64
os: linux
steps:
- name: build
image: golang:1.22.7
commands:
- sh scripts/build.sh
environment:
GOARCH: arm64
GOOS: linux
- name: publish
image: plugins/docker:18
settings:
auto_tag: true
auto_tag_suffix: linux-arm64
dockerfile: docker/Dockerfile.server.linux.arm64
repo: drone/drone
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
event:
- push
- tag
depends_on:
- linux-amd64
---
kind: pipeline
type: docker
name: manifest
steps:
- name: publish
image: plugins/manifest:1.2
settings:
auto_tag: true
ignore_missing: true
spec: docker/manifest.server.tmpl
username:
from_secret: docker_username
password:
from_secret: docker_password
trigger:
event:
- push
- tag
depends_on:
- linux-arm64

View File

@ -1,68 +0,0 @@
#!/bin/bash
# Copyright 2025 Harness Inc. All rights reserved.
# Use of this source code is governed by the PolyForm Free Trial 1.0.0 license
# that can be found in the licenses directory at the root of this repository, also available at
# https://polyformproject.org/wp-content/uploads/2020/05/PolyForm-Free-Trial-1.0.0.txt.
###################################################
# Purpose
# The purpose of this script is to facilitate auto-tagging of
# Jira tickets with fix-versions. There are two tricky parts to auto-tagging:
#
# 1. Given a change set (PR diff), what constitutes a change to a service?
# 2. Given you've determined a set of files has changed a service, which service was changed?
#
# This script endeavors to answer question number one - which files constitute a change to a service
# Given an input file which is the git diff from a PR
# this script should determine what file changes in the diff
# constitute a material change to a service. At the time
# of this writing, this currently only identifies java and go
# files, and if those files are in the change list, then those
# file names are returned in the output file.
#
# Inputs
# $1 - File containing unique changed files
# $2 - the output file which should ultimately contain file names from the diff that affect a service
#
# See BT-10437 for more information
#
# Called by https://harness0.harness.io/ng/account/l7B_kbSEQD2wjrM7PShm5w/all/orgs/Audit/projects/Engops_Audit/pipelines/PRMergedGithub/pipeline-studio/?storeType=INLINE
# Unlike other scripts for detecting changes, this one doesn't receive a git diff, rather a unique list of files changed by the git update.
#
# Owner: Engops
# Author: Marc Batchelor
###################################################
echo "Arguments: " $*
uniqueFileNamesFile=$1
sourceDiffNames=$2
if [ -z "$uniqueFileNamesFile" ]; then
echo "Missing input PR Difference file."
exit 1
fi
if [ ! -f "$uniqueFileNamesFile" ]; then
echo "Input file $uniqueFileNamesFile does not exist and is required."
exit 2
fi
if [ -z "$sourceDiffNames" ]; then
echo "Missing output file."
exit 3
fi
if [ ! -f "$sourceDiffNames" ]; then
echo "File $sourceDiffNames does not exist and is required."
exit 4
fi
##### Detect git diff file, or processed filenames only
isDiffFile=$(grep -E "^diff --git a\/" "$uniqueFileNamesFile" | wc -l)
if [ $isDiffFile -gt 0 ]; then
echo "Received a diff file... fix it to be a filenames only file"
fileNamesOnlyVar=$(cat "$uniqueFileNamesFile"|grep -E "^diff --git" | sed 's/diff --git a\///' | sed 's/ b\/.*$//' | sort -u)
echo -e "$fileNamesOnlyVar">"$uniqueFileNamesFile"
fi
# Java files (and other files) which end up in jars - these are kept in .../src/main/x/x/x/*
cat "$uniqueFileNamesFile" | grep -E ".*.java$" | grep -v "/test/" > $sourceDiffNames
# go files (without tests)
cat "$uniqueFileNamesFile" | grep -E ".*.go$|.*.mod$" | grep -v "test_" >> $sourceDiffNames
# Other source files
cat "$uniqueFileNamesFile" | grep -E ".*.(Dockerfile|Dockerfile.cov|Dockerfile.dev|bazel|c|cc|conf|css|ejs|eslintrc|gitmodules|go|golang|gradle|gv|graphql|h|html|iml|js|json|less|mod|pipeline|mustache|pl|png|properties|ps1|proto|py|pyc|qbg|repo|rs|sh||sha256|sql|sum|svg|tf|tgz|tmpl|tpl|ts|tsx|xml|yaml|yml)$" >> $sourceDiffNames

View File

@ -1,38 +0,0 @@
#!/usr/bin/env sh
echo Running pre-commit hook
# Check for required binaries, otherwise don't run
if ! command -v grep &> /dev/null
then
echo "grep could not be found - skipping pre-commit"
exit 0
fi
if ! command -v sed &> /dev/null
then
echo "sed could not be found - skipping pre-commit"
exit 0
fi
if ! command -v xargs &> /dev/null
then
echo "xargs could not be found - skipping pre-commit"
exit 0
fi
# Run pre-commit, this checks if we changed any golang files and runs the checks.
# The files are then git-added
FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep .go | sed 's| |\\ |g')
if [ -n "$FILES" ]; then
make format
make lint
if [ $? -ne 0 ]; then
echo "Error running make check - please fix before committing"
echo "if this is a mistake you can skip the checks with 'git commit --no-verify'"
exit 1
fi
echo "$FILES" | xargs git add
fi
exit 0

3
.github/code_of_conduct.md vendored Normal file
View File

@ -0,0 +1,3 @@
## Drone Community Code of Conduct
Drone follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

35
.github/contributing.md vendored Normal file
View File

@ -0,0 +1,35 @@
# Contributing
Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.
Following these guidelines helps to communicate that you respect the time of the developers maintaining this project. In return, they should reciprocate that respect in addressing your issue or assessing patches and features.
## Support Requests
The [mailing list](https://discourse.drone.io) is the preferred channel for support requests. Please do not use the issue tracker for personal support requests.
## Feature Requests
Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of this project. It is up to you to make a strong case to convince the developers of the merits of this feature. Please provide as much detail and context as possible.
## Compatibility
This project maintains a strong commitment to backward compatibility. Changing the runtime behavior or configuration in a manner that breaks the public contract should always be avoided.
## Dependencies
This project does not attempt to track the latest version for each dependency. Please only apply the minimal dependency changes required for your patch to work. _If it ain't broke, don't fix it._
## Pull Requests
Please discuss on our [mailing list](https://discourse.drone.io) before embarking on any significant pull request, otherwise you risk spending time working on something the developers might not want to merge into the project.
Pull requests should remain focused in scope and avoid containing unrelated commits. For example, a pull request could add a feature, fix a bug, or format code; but not a mixture.
When you are ready to submit a pull request you can use the below checklist to to increase the likelihood of your pull request being accepted in a timely manner:
- Run the unit tests.
- Format the code.
- Include entry in [CHANGELOG.md](../CHANGELOG.md) that describes the change.
- Include unit tests when you contribute a new feature.
- Include unit tests when you contribute a bug fix to prevent regressions.

16
.github/issue_template.md vendored Normal file
View File

@ -0,0 +1,16 @@
<!-- PLEASE READ BEFORE DELETING
Bugs or Issues? Please create a new topic in our Discourse forum.
We are migrating all Drone repositories to Discourse for bug tracking.
New GitHub issues may be automatically deleted.
https://community.harness.io/
https://community.harness.io/c/bugs/17
https://community.harness.io/c/ideas/11
Failing Builds? Please do not use GitHub issues for generic support
questions. Instead please use Stack Overflow:
http://stackoverflow.com/questions/tagged/drone.io
-->

27
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,27 @@
## Commit Checklist
Thank you for creating a pull request! To help us review / merge this can you make sure that your PR adheres as much as possible to the following.
### The Basics
- Commit is a single logical unit of work, only use multiple commits if doing different tasks
- Commit does not include commented out code or unneeded files
- rebase of main branch
### The Content
- Must include testing for bug or feature
- Must include appropriate documentation changes if it is introducing a new feature or changing existing functionality
- Must pass existing test suites
### The Commit Message
- Short meaningful description (ex: remove deprecated steps)
- Uses the imperative, present tense: "change", not "changed" or "changes"
- Includes motivation for the change, and contrasts its implementation with the previous behavior
### The Pull Request
- What is the reason for this change
- Example usage of the failure for a bug, or configuration and expected output for a feature
- Steps to test the change

122
.github/readme.md vendored Normal file
View File

@ -0,0 +1,122 @@
# [Drone](https://www.drone.io/) <img src="https://github.com/drone/brand/blob/master/screenshots/screenshot_build_success.png" style="max-width:100px;" />
**Welcome to the Drone codebase, we are thrilled to have you here!**
## What is Drone?
Drone is a continuous delivery system built on container technology. Drone uses a simple YAML build file, to define and execute build pipelines inside Docker containers.
## Table of Contents
- [What is Drone?](#what-is-drone)
- [Table of Contents](#table-of-contents)
- [Community and Support](#community-and-support)
- [Contributing](#contributing)
- [Code of Conduct](#code-of-conduct)
- [Setup Documentation](#setup-documentation)
- [Usage Documentation](#usage-documentation)
- [Example `.drone.yml` build file](#example-droneyml-build-file)
- [Plugin Index](#plugin-index)
- [Documentation and Other Links](#documentation-and-Other-Links)
## Community and Support
[Harness Community Slack](https://join.slack.com/t/harnesscommunity/shared_invite/zt-y4hdqh7p-RVuEQyIl5Hcx4Ck8VCvzBw) - Join the #drone slack channel to connect with our engineers and other users running Drone CI.
</br>
[Harness Community Forum](https://community.harness.io/) - Ask questions, find answers, and help other users.
</br>
[Report A Bug](https://community.harness.io/c/bugs/17) - Find a bug? Please report in our forum under Drone Bugs. Please provide screenshots and steps to reproduce.
</br>
[Events](https://www.meetup.com/harness/) - Keep up to date with Drone events and check out previous events [here](https://www.youtube.com/watch?v=Oq34ImUGcHA&list=PLXsYHFsLmqf3zwelQDAKoVNmLeqcVsD9o).
## Contributing
We encourage you to contribute to Drone! Whether that's joining in on the community slack or discourse, or contributing pull requests / documentation changes or raising issues.
## Code of Conduct
Drone follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
### Setup Documentation
This section of the [documentation](http://docs.drone.io/installation/) will help you install and configure the Drone Server and one or many Runners. A runner is a standalone daemon that polls the server for pending pipelines to execute.
### Usage Documentation
Our [documentation](http://docs.drone.io/getting-started/) can help you get started with the different types of pipelines/builds. There are different runners / plugins / extensions designed for different use cases to help make an efficient and simple build pipeline
### Plugin Index
Plugins are used in build steps to perform actions, eg send a message to slack or push a container to a registry. We have an extensive list of community plugins to customize your build pipeline, you can find those [here](http://plugins.drone.io/).
### Example `.drone.yml` build file.
This build file contains a single pipeline (you can have multiple pipelines too) that builds a go application. The front end with npm. Publishes the docker container to a registry and announces the results to a slack room.
```YAML
name: default
kind: pipeline
type: docker
steps:
- name: backend
image: golang
commands:
- go get
- go build
- go test
- name: frontend
image: node:6
commands:
- npm install
- npm test
- name: publish
image: plugins/docker
settings:
repo: octocat/hello-world
tags: [ 1, 1.1, latest ]
registry: index.docker.io
- name: notify
image: plugins/slack
settings:
channel: developers
username: drone
```
## Documentation and Other Links
* Setup Documentation [docs.drone.io/installation](http://docs.drone.io/installation/)
* Usage Documentation [docs.drone.io/getting-started](http://docs.drone.io/getting-started/)
* Plugin Index [plugins.drone.io](http://plugins.drone.io/)
* Getting Help [discourse.drone.io](https://discourse.drone.io)
* Build the Enterprise Edition [BUILDING](https://github.com/drone/drone/blob/master/BUILDING)
* Build the Community Edition [BUILDING_OSS](https://github.com/drone/drone/blob/master/BUILDING_OSS)
## Building from source
We have two versions available: the [Enterprise Edition](https://github.com/drone/drone/blob/master/BUILDING) and the [Community Edition](https://github.com/drone/drone/blob/master/BUILDING_OSS)
## Release procedure
Run the changelog generator.
```BASH
docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator -u drone -p drone -t <secret github token>
```
You can generate a token by logging into your GitHub account and going to Settings -> Personal access tokens.
Next we tag the PR's with the fixes or enhancements labels. If the PR does not fulfill the requirements, do not add a label.
**Before moving on make sure to update the version file `version/version.go && version/version_test.go`.**
Run the changelog generator again with the future version according to semver.
```BASH
docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator -u harness -p drone -t <secret token> --future-release v1.0.0
```
Create your pull request for the release. Get it merged then tag the release.
[⬆ Back to Top](#table-of-contents)

36
.github/security.md vendored Normal file
View File

@ -0,0 +1,36 @@
# Security Policies and Procedures
This document outlines security procedures and general policies for this project.
* [Reporting a Bug](#reporting-a-bug)
* [Disclosure Policy](#disclosure-policy)
* [Comments on this Policy](#comments-on-this-policy)
## Reporting a Bug
Report security bugs by emailing the lead maintainer at security@drone.io.
The lead maintainer will acknowledge your email within 48 hours, and will send a
more detailed response within 48 hours indicating the next steps in handling
your report. After the initial reply to your report, the security team will
endeavor to keep you informed of the progress towards a fix and full
announcement, and may ask for additional information or guidance.
Report security bugs in third-party software to the person or team maintaining
that software.
## Disclosure Policy
When the security team receives a security bug report, they will assign it to a
primary handler. This person will coordinate the fix and release process,
involving the following steps:
* Confirm the problem and determine the affected versions.
* Audit code to find any potential similar problems.
* Prepare fixes for all releases still under maintenance. These fixes will be
released as fast as possible to DockerHub.
## Comments on this Policy
If you have suggestions on how this process could be improved please submit a
pull request.

View File

@ -1,66 +0,0 @@
name: CI Linter pipeline
on:
push:
tags:
- v*
branches:
- master
- main
pull_request:
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
# pull-requests: read
jobs:
web:
name: CI linter for js/ts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: "npm"
cache-dependency-path: web/yarn.lock
- name: install, lint, and build web app
working-directory: web
run: |
yarn install
yarn check:all
yarn build
gitness:
name: CI linter for go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.23'
- name: get dependencies
run: |
mkdir -p ./web/dist
touch ./web/dist/empty.txt
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v1.64.5
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
# Optional: if set to true then the all caching functionality will be complete disabled,
# takes precedence over all other caching options.
# skip-cache: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

View File

@ -0,0 +1,3 @@
since-tag=v2.0.4
issues=false

44
.gitignore vendored
View File

@ -1,37 +1,13 @@
.DS_Store
NOTES*
.vscode
__debug_bin
_research
.env
*.sqlite
*.sqlite3
web/node_modules
web/dist
web/coverage
web/.yalc
web/yalc.lock
yarn-error*
release
*.txt
*.out
*.key
.env
.env.*
release/
scripts/*.go
docker/**/data
TODO*
.idea
.vscode/settings.json
coverage.out
gitness.session.sql
web/cypress/node_modules
*.rsa
*.rsa.pub
node_modules/
dist
.yalc
yalc.lock
node_modules
.cursor
# ignore any executables we build
/gitness
/registry/logs/*
/distribution-spec
/registry/distribution-spec
/app/store/database/test.db
# adding support for .http files
http-client.private.env.json

View File

@ -1,485 +0,0 @@
version: "2"
linters:
default: none
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- contextcheck
- copyloopvar
- durationcheck
- errcheck
- errname
- errorlint
- exhaustive
- forbidigo
- goconst
- gocritic
- godot
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- govet
- ineffassign
- lll
- makezero
- misspell
- nakedret
- nestif
- nilerr
- nilnil
- noctx
- nosprintfhostport
- predeclared
- promlinter
- reassign
- revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- tagliatelle
- tparallel
- unconvert
- unparam
- unused
- usestdlibvars
- wastedassign
- whitespace
settings:
revive:
rules:
- name: var-naming
severity: warning
disabled: true
errcheck:
check-type-assertions: true
gocritic:
settings:
captLocal:
paramsOnly: false
underef:
skipRecvDeref: false
goheader:
template: |-
Copyright 2023 Harness, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
gomodguard:
blocked:
modules:
- github.com/golang/protobuf:
recommendations:
- google.golang.org/protobuf
reason: see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules
- github.com/satori/go.uuid:
recommendations:
- github.com/google/uuid
reason: satori's package is not maintained
- github.com/gofrs/uuid:
recommendations:
- github.com/google/uuid
reason: 'see recommendation from dev-infra team: https://confluence.gtforge.com/x/gQI6Aw'
govet:
disable:
- fieldalignment
enable-all: true
settings:
shadow:
strict: true
nakedret:
max-func-lines: 30
rowserrcheck:
packages:
- github.com/jmoiron/sqlx
staticcheck:
checks:
- all
- -SA1019
- -QF1008
tagliatelle:
case:
rules:
avro: snake
bson: snake
db: snake
json: snake
mapstructure: snake
xml: snake
yaml: snake
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- govet
text: 'shadow: declaration of "(err|ctx|ok)" shadows declaration at'
- linters:
- lll
source: ^//\s*go:generate\s
- linters:
- gomoddirectives
text: 'local replacement are not allowed: github.com/harness/gitness'
- linters:
- gomoddirectives
text: 'replacement are not allowed: github.com/docker/docker'
- linters:
- godot
source: (noinspection|TODO)
- linters:
- gocritic
source: //noinspection
- linters:
- errorlint
source: ^\s+if _, ok := err\.\([^.]+\.InternalError\); ok {
- linters:
- forbidigo
path: ^cli/
- linters:
- revive
- staticcheck
- tagliatelle
path: ^registry/app/manifest/.*
- linters:
- errorlint
path: ^registry/app/dist_temp/.*
- linters:
- gocritic
path: ^registry/app/driver/filesystem/.*
- linters:
- gocognit
- gosec
- nestif
path: ^registry/app/driver/s3-aws/.*
- linters:
- goheader
path: ^registry/app/remote/clients/registry/interceptor/interceptor.go
- linters:
- goheader
path: ^registry/app/common/http/modifier/modifier.go
- linters:
- goheader
path: ^registry/app/driver/fileinfo.go
- linters:
- goheader
path: ^registry/app/driver/storagedriver.go
- linters:
- goheader
path: ^registry/app/driver/walk.go
- linters:
- goheader
path: ^registry/app/dist_temp/challenge/addr.go
- linters:
- goheader
path: ^registry/app/dist_temp/challenge/authchallenge.go
- linters:
- goheader
path: ^registry/app/dist_temp/challenge/authchallenge_test.go
- linters:
- goheader
path: ^registry/app/dist_temp/requestutil/util.go
- linters:
- goheader
path: ^registry/app/dist_temp/requestutil/util_test.go
- linters:
- goheader
path: ^registry/app/pkg/commons/zipreader/*
- linters:
- goheader
path: ^registry/app/manifest/descriptor.go
- linters:
- goheader
path: ^registry/app/manifest/doc.go
- linters:
- goheader
path: ^registry/app/manifest/errors.go
- linters:
- goheader
path: ^registry/app/manifest/manifests.go
- linters:
- goheader
path: ^registry/app/manifest/versioned.go
- linters:
- goheader
path: ^registry/app/common/lib/authorizer.go
- linters:
- goheader
path: ^registry/app/common/lib/link.go
- linters:
- goheader
path: ^registry/app/common/http/tls.go
- linters:
- goheader
path: ^registry/app/common/http/transport.go
- linters:
- goheader
path: ^registry/app/common/http/transport_test.go
- linters:
- goheader
path: ^registry/app/manifest/schema2/manifest.go
- linters:
- goheader
path: ^registry/app/manifest/schema2/manifest_test.go
- linters:
- goheader
path: ^registry/app/manifest/ocischema/index.go
- linters:
- goheader
path: ^registry/app/manifest/ocischema/manifest.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/null/authorizer.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/basic/authorizer.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/basic/authorizer_test.go
- linters:
- goheader
path: ^registry/app/common/lib/errors/const.go
- linters:
- goheader
path: ^registry/app/common/lib/errors/errors.go
- linters:
- goheader
path: ^registry/app/common/lib/errors/stack.go
- linters:
- goheader
path: ^registry/app/common/lib/errors/stack_test.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/bearer/authorizer.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/bearer/cache.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/bearer/scope.go
- linters:
- goheader
path: ^registry/app/manifest/manifestlist/manifestlist.go
- linters:
- goheader
path: ^registry/app/manifest/manifestlist/manifestlist_test.go
- linters:
- goheader
path: ^registry/app/driver/factory/factory.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/context.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/doc.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/http.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/logger.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/trace.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/util.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/version.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/http_test.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/trace_test.go
- linters:
- goheader
path: ^registry/app/dist_temp/dcontext/version_test.go
- linters:
- goheader
path: ^registry/app/driver/base/base.go
- linters:
- goheader
path: ^registry/app/driver/base/regulator.go
- linters:
- goheader
path: ^registry/app/driver/base/regulator_test.go
- linters:
- goheader
path: ^registry/app/storage/blobs.go
- linters:
- goheader
path: ^registry/app/storage/blobwriter.go
- linters:
- goheader
path: ^registry/app/storage/blobwriter_resumable.go
- linters:
- goheader
path: ^registry/app/storage/errors.go
- linters:
- goheader
path: ^registry/app/storage/filereader.go
- linters:
- goheader
path: ^registry/app/storage/gcstoragelient.go
- linters:
- goheader
path: ^registry/app/storage/io.go
- linters:
- goheader
path: ^registry/app/storage/middleware.go
- linters:
- goheader
path: ^registry/app/storage/ociblobstore.go
- linters:
- goheader
path: ^registry/app/storage/paths.go
- linters:
- goheader
path: ^registry/app/storage/storageservice.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/client.go
- linters:
- goheader
path: ^registry/app/remote/adapter/adapter.go
- linters:
- goheader
path: ^registry/app/remote/clients/registry/auth/authorizer.go
- linters:
- goheader
path: ^registry/app/driver/s3-aws/s3.go
- linters:
- goheader
path: ^registry/app/driver/s3-aws/s3_v2_signer.go
- linters:
- goheader
path: ^registry/app/driver/filesystem/driver.go
- linters:
- goheader
path: ^registry/app/pkg/docker/app.go
- linters:
- goheader
path: ^registry/app/pkg/docker/catalog.go
- linters:
- goheader
path: ^registry/app/pkg/docker/compat.go
- linters:
- goheader
path: ^registry/app/pkg/docker/context.go
- linters:
- goheader
path: ^registry/app/pkg/docker/controller.go
- linters:
- goheader
path: ^registry/app/pkg/docker/local.go
- linters:
- goheader
path: ^registry/app/pkg/docker/manifest_service.go
- linters:
- goheader
path: ^registry/app/pkg/docker/remote.go
- linters:
- goheader
path: ^registry/app/remote/adapter/dockerhub/adapter.go
- linters:
- goheader
path: ^registry/app/remote/adapter/awsecr/adapter.go
- linters:
- goheader
path: ^registry/app/remote/adapter/maven/adapter.go
- linters:
- goheader
path: ^registry/app/remote/adapter/awsecr/auth.go
- linters:
- goheader
path: ^registry/app/remote/adapter/dockerhub/client.go
- linters:
- goheader
path: ^registry/app/remote/adapter/dockerhub/consts.go
- linters:
- goheader
path: ^registry/app/driver/testsuites/testsuites.go
- linters:
- goheader
path: ^registry/app/dist_temp/errcode/errors.go
- linters:
- goheader
path: ^registry/app/dist_temp/errcode/handler.go
- linters:
- goheader
path: ^registry/app/dist_temp/errcode/register.go
- linters:
- goheader
path: ^registry/app/remote/controller/proxy/controller.go
- linters:
- goheader
path: ^registry/app/remote/controller/proxy/inflight.go
- linters:
- goheader
path: ^registry/app/remote/controller/proxy/local.go
- linters:
- goheader
path: ^registry/app/remote/controller/proxy/remote.go
- linters:
- goheader
path: ^registry/app/remote/controller/proxy/inflight_test.go
- linters:
- goheader
path: ^registry/app/remote/adapter/native/adapter.go
- linters:
- gosec
path: ^registry/app/storage/blobStore.go
- linters:
- lll
- tagliatelle
path: ^registry/app/metadata/nuget/metadata.go
- linters:
- errcheck
- gocritic
- godot
- goheader
- lll
path: ^registry/app/api/controller/mocks/
paths:
- third_party$
- builtin$
- examples$
issues:
max-same-issues: 10
formatters:
enable:
- gci
- goimports
settings:
gci:
sections:
- standard
- prefix(github.com/harness/gitness)
- default
- blank
- dot
custom-order: true
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -1,19 +0,0 @@
GITNESS_TRACE=true
GITNESS_GIT_TRACE=true
GITNESS_PRINCIPAL_ADMIN_EMAIL=admin@gitness.io
GITNESS_PRINCIPAL_ADMIN_PASSWORD=changeit
GITNESS_WEBHOOK_ALLOW_LOOPBACK=true
GITNESS_METRIC_ENABLED=false
GITNESS_HTTP_HOST=localhost
GITNESS_GITSPACE_ENABLE=true
GITNESS_DEBUG=true
GITNESS_DOCKER_API_VERSION=1.45
GITNESS_SSH_ENABLE=true
GITNESS_SSH_HOST=localhost
GITNESS_SSH_PORT=2222
GITNESS_REGISTRY_STORAGE_TYPE=filesystem
GITNESS_REGISTRY_FILESYSTEM_ROOT_DIRECTORY=/tmp
#GITNESS_DATABASE_DRIVER=postgres
#GITNESS_DATABASE_DATASOURCE=postgres://postgres:postgres@localhost:5432/gitness?sslmode=disable

View File

@ -1,28 +0,0 @@
GET {{baseurl}}/repos/root/{{repo}}/+/diff/{{targetBranch}}...{{sourceBranch}}
Accept: text/plain
Authorization: {{token}}
### Get diff ignore white space
GET {{baseurl}}/repos/root/{{repo}}/+/diff/{{targetBranch}}...{{sourceBranch}}?ignore_whitespace=true
Accept: text/plain
Authorization: {{token}}
### Get diff with hidden white spaces
GET {{baseurl}}/repos/root/{{repo}}/+/commits/{{commit}}/diff
Accept: text/plain
Authorization: {{token}}
### Get commit diff ignore white space
GET {{baseurl}}/repos/root/{{repo}}/+/commits/{{commit}}/diff?ignore_whitespace=true
Accept: text/plain
Authorization: {{token}}
### Get diff stats
GET {{baseurl}}/repos/root/{{repo}}/+/diff-stats/{{targetBranch}}...{{sourceBranch}}
Accept: text/plain
Authorization: {{token}}
### Get diff stats ignore white space
GET {{baseurl}}/repos/root/{{repo}}/+/diff-stats/{{targetBranch}}...{{sourceBranch}}?ignore_whitespace=true
Accept: text/plain
Authorization: {{token}}

View File

@ -1,5 +0,0 @@
{
"dev": {
"baseurl": "http://localhost:3000/api/v1"
}
}

View File

@ -1,7 +0,0 @@
POST {{baseurl}}/login
Content-Type: application/json
{
"login_identifier": "{{login_identifier}}",
"password": "{{password}}"
}

View File

@ -1,4 +0,0 @@
### Get metric for space
GET {{baseurl}}/spaces/root/+/usage/metric
Authorization: {{token}}

18
.vscode/launch.json vendored
View File

@ -1,18 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
//TODO update this when we are ready to cutover
"name": "Gitness",
"type": "go",
"request": "launch",
"mode": "auto",
"buildFlags": "",
"program": "cmd/gitness",
"args": ["server", "../../.local.env"]
}
]
}

11
BUILDING Normal file
View File

@ -0,0 +1,11 @@
1. Clone the repository
2. Install go 1.11 or later with Go modules enabled
3. Install binaries to $GOPATH/bin
go install github.com/drone/drone/cmd/drone-server
4. Start the server at localhost:8080
export DRONE_GITHUB_CLIENT_ID=...
export DRONE_GITHUB_CLIENT_SECRET=...
drone-server

11
BUILDING_OSS Normal file
View File

@ -0,0 +1,11 @@
1. Clone the repository
2. Install go 1.11 or later with Go modules enabled
3. Install binaries to $GOPATH/bin
go install -tags "oss nolimit" github.com/drone/drone/cmd/drone-server
4. Start the server at localhost:8080
export DRONE_GITHUB_CLIENT_ID=...
export DRONE_GITHUB_CLIENT_SECRET=...
drone-server

797
CHANGELOG.md Normal file
View File

@ -0,0 +1,797 @@
# Changelog
## [v2.20.0](https://github.com/harness/drone/tree/v2.20.0) (2023-08-21)
[Full Changelog](https://github.com/harness/drone/compare/v2.19.0...v2.20.0)
**Implemented enhancements:**
- + sync gitea redirecturl config from gitee for customize login redire… [\#3319](https://github.com/harness/drone/pull/3319) ([fireinice](https://github.com/fireinice))
**Fixed bugs:**
- \(CI-8780\) set approved stages to waiting, if they have stage depende… [\#3355](https://github.com/harness/drone/pull/3355) ([tphoney](https://github.com/tphoney))
## [v2.19.0](https://github.com/harness/drone/tree/v2.19.0) (2023-08-15)
[Full Changelog](https://github.com/harness/drone/compare/scheduler_experiment...v2.19.0)
**Implemented enhancements:**
- Support arbitrary action value from parameter in query string [\#3341](https://github.com/harness/drone/pull/3341) ([filippopisano](https://github.com/filippopisano))
**Fixed bugs:**
- bump drone-ui to 2.11.5 [\#3350](https://github.com/harness/drone/pull/3350) ([d1wilko](https://github.com/d1wilko))
- bump drone-ui to 2.11.4 [\#3349](https://github.com/harness/drone/pull/3349) ([d1wilko](https://github.com/d1wilko))
- \(fix\) prevent scheduler deadlock [\#3344](https://github.com/harness/drone/pull/3344) ([tphoney](https://github.com/tphoney))
- bump drone-ui to 2.11.3 [\#3337](https://github.com/harness/drone/pull/3337) ([d1wilko](https://github.com/d1wilko))
**Merged pull requests:**
- \(maint\) prep for v2.19.0 [\#3352](https://github.com/harness/drone/pull/3352) ([tphoney](https://github.com/tphoney))
- remove repetitive words [\#3342](https://github.com/harness/drone/pull/3342) ([cuishuang](https://github.com/cuishuang))
- Revert "fix scheduler queue deadlock" [\#3331](https://github.com/harness/drone/pull/3331) ([tphoney](https://github.com/tphoney))
## [scheduler_experiment](https://github.com/harness/drone/tree/scheduler_experiment) (2023-07-05)
[Full Changelog](https://github.com/harness/drone/compare/v2.18.0...scheduler_experiment)
**Fixed bugs:**
- fix scheduler queue deadlock [\#3330](https://github.com/harness/drone/pull/3330) ([tphoney](https://github.com/tphoney))
## [v2.18.0](https://github.com/harness/drone/tree/v2.18.0) (2023-07-04)
[Full Changelog](https://github.com/harness/drone/compare/v2.17.0...v2.18.0)
**Implemented enhancements:**
- support custom pipeline message [\#3294](https://github.com/harness/drone/pull/3294) ([zc2638](https://github.com/zc2638))
**Fixed bugs:**
- bump drone-ui to 2.11.2 [\#3327](https://github.com/harness/drone/pull/3327) ([d1wilko](https://github.com/d1wilko))
- Fix comment errors [\#3302](https://github.com/harness/drone/pull/3302) ([weidongkl](https://github.com/weidongkl))
**Merged pull requests:**
- v2.18.0 release prep [\#3328](https://github.com/harness/drone/pull/3328) ([tphoney](https://github.com/tphoney))
## [v2.17.0](https://github.com/harness/drone/tree/v2.17.0) (2023-04-25)
[Full Changelog](https://github.com/harness/drone/compare/v2.16.0...v2.17.0)
**Implemented enhancements:**
- Add `authtype` to logging middleware [\#3310](https://github.com/harness/drone/pull/3310) ([colinhoglund](https://github.com/colinhoglund))
- Add config for the buffer [\#3308](https://github.com/harness/drone/pull/3308) ([TheJokersThief](https://github.com/TheJokersThief))
**Fixed bugs:**
- store/card: fix dropped error [\#3300](https://github.com/harness/drone/pull/3300) ([alrs](https://github.com/alrs))
- bump drone-ui to 2.9.1 [\#3298](https://github.com/harness/drone/pull/3298) ([d1wilko](https://github.com/d1wilko))
- Starlark: Update `go.starlark.net` dependency [\#3284](https://github.com/harness/drone/pull/3284) ([dsotirakis](https://github.com/dsotirakis))
**Merged pull requests:**
- release prep for v2.17.0 [\#3316](https://github.com/harness/drone/pull/3316) ([eoinmcafee00](https://github.com/eoinmcafee00))
- bump drone-ui to 2.11.1 [\#3315](https://github.com/harness/drone/pull/3315) ([d1wilko](https://github.com/d1wilko))
- bump drone-ui to 2.11.0 [\#3313](https://github.com/harness/drone/pull/3313) ([d1wilko](https://github.com/d1wilko))
- bump drone-ui to 2.10.0 [\#3311](https://github.com/harness/drone/pull/3311) ([d1wilko](https://github.com/d1wilko))
- \(maint\) move to use the arm64 pool [\#3296](https://github.com/harness/drone/pull/3296) ([tphoney](https://github.com/tphoney))
## [v2.16.0](https://github.com/harness/drone/tree/v2.16.0) (2022-12-15)
[Full Changelog](https://github.com/harness/drone/compare/v2.15.0...v2.16.0)
**Implemented enhancements:**
- Make Starlark file size limit configurable [\#3291](https://github.com/harness/drone/pull/3291) ([andrii-kasparevych](https://github.com/andrii-kasparevych))
- Enhance status check label for promotions [\#3263](https://github.com/harness/drone/pull/3263) ([michelangelomo](https://github.com/michelangelomo))
**Fixed bugs:**
- \(bugfix\) bump go-scm to v1.28.0 [\#3290](https://github.com/harness/drone/pull/3290) ([tphoney](https://github.com/tphoney))
**Merged pull requests:**
- \(maint\) 2.16.0 release prep [\#3295](https://github.com/harness/drone/pull/3295) ([tphoney](https://github.com/tphoney))
## [v2.15.0](https://github.com/harness/drone/tree/v2.15.0) (2022-10-28)
[Full Changelog](https://github.com/harness/drone/compare/v2.14.0...v2.15.0)
**Implemented enhancements:**
- bump ui version [\#3279](https://github.com/harness/drone/pull/3279) ([d1wilko](https://github.com/d1wilko))
- Add endpoint for allowing admins to force rotate a user's token [\#3272](https://github.com/harness/drone/pull/3272) ([ShiftedMr](https://github.com/ShiftedMr))
**Merged pull requests:**
- release prep v2.15.0 [\#3281](https://github.com/harness/drone/pull/3281) ([d1wilko](https://github.com/d1wilko))
## [v2.14.0](https://github.com/harness/drone/tree/v2.14.0) (2022-10-18)
[Full Changelog](https://github.com/harness/drone/compare/v2.13.0...v2.14.0)
**Implemented enhancements:**
- \(DRON-418\) send webhook and set status for failed builds [\#3266](https://github.com/harness/drone/pull/3266) ([tphoney](https://github.com/tphoney))
**Merged pull requests:**
- v2.14.0 release prep [\#3275](https://github.com/harness/drone/pull/3275) ([d1wilko](https://github.com/d1wilko))
## [v2.13.0](https://github.com/harness/drone/tree/v2.13.0) (2022-09-21)
[Full Changelog](https://github.com/harness/drone/compare/v2.12.1...v2.13.0)
**Implemented enhancements:**
- feat: update drone-yaml module [\#3249](https://github.com/harness/drone/pull/3249) ([jimsheldon](https://github.com/jimsheldon))
- support time zone [\#3241](https://github.com/harness/drone/pull/3241) ([zc2638](https://github.com/zc2638))
**Fixed bugs:**
- update discourse.drone.io to community.harness.io [\#3261](https://github.com/harness/drone/pull/3261) ([kit101](https://github.com/kit101))
- \(DRON-392\) cascade deletes on purge [\#3243](https://github.com/harness/drone/pull/3243) ([tphoney](https://github.com/tphoney))
- Template converter, don't skip .yaml extension. [\#3242](https://github.com/harness/drone/pull/3242) ([staffanselander](https://github.com/staffanselander))
**Merged pull requests:**
- v2.13.0 release prep [\#3268](https://github.com/harness/drone/pull/3268) ([tphoney](https://github.com/tphoney))
- \(maint\) disable arm builds [\#3262](https://github.com/harness/drone/pull/3262) ([tphoney](https://github.com/tphoney))
- Update links to discourse in issue template [\#3233](https://github.com/harness/drone/pull/3233) ([alikhil](https://github.com/alikhil))
## [v2.12.1](https://github.com/harness/drone/tree/v2.12.1) (2022-06-15)
[Full Changelog](https://github.com/harness/drone/compare/v2.12.0...v2.12.1)
**Fixed bugs:**
- \(bug\) - fix original template scripts & remove amend scripts [\#3229](https://github.com/harness/drone/pull/3229) ([eoinmcafee00](https://github.com/eoinmcafee00))
- \(bug\) - remove unique index on template name [\#3226](https://github.com/harness/drone/pull/3226) ([eoinmcafee00](https://github.com/eoinmcafee00))
- Added OAuth2 token refresher for Gitlab [\#3215](https://github.com/harness/drone/pull/3215) ([EndymionWight](https://github.com/EndymionWight))
**Merged pull requests:**
- release prep for v2.12.1 [\#3232](https://github.com/harness/drone/pull/3232) ([eoinmcafee00](https://github.com/eoinmcafee00))
- \(maint\) fix starlark test on windows [\#3230](https://github.com/harness/drone/pull/3230) ([tphoney](https://github.com/tphoney))
- \(maint\) fix unit tests so they pass on windows [\#3228](https://github.com/harness/drone/pull/3228) ([tphoney](https://github.com/tphoney))
- Update Readme to Fix Typo [\#3223](https://github.com/harness/drone/pull/3223) ([hrittikhere](https://github.com/hrittikhere))
- \(bug\) add unit test for comments in template file [\#3221](https://github.com/harness/drone/pull/3221) ([eoinmcafee00](https://github.com/eoinmcafee00))
- Bump scm version to v1.24.0 [\#3219](https://github.com/harness/drone/pull/3219) ([kit101](https://github.com/kit101))
## [v2.12.0](https://github.com/harness/drone/tree/v2.12.0) (2022-05-16)
[Full Changelog](https://github.com/harness/drone/compare/v2.11.1...v2.12.0)
**Implemented enhancements:**
- bump SCM version to v1.21.1 [\#3204](https://github.com/harness/drone/pull/3204) ([d1wilko](https://github.com/d1wilko))
- bump ui version [\#3202](https://github.com/harness/drone/pull/3202) ([d1wilko](https://github.com/d1wilko))
**Fixed bugs:**
- \(fix\) update drone ui to 2.8.2 [\#3211](https://github.com/harness/drone/pull/3211) ([tphoney](https://github.com/tphoney))
- \(dron-267\) correctly set parent for promotion retry [\#3210](https://github.com/harness/drone/pull/3210) ([tphoney](https://github.com/tphoney))
**Merged pull requests:**
- release prep v2.12.0 [\#3214](https://github.com/harness/drone/pull/3214) ([tphoney](https://github.com/tphoney))
- fixing URL [\#3208](https://github.com/harness/drone/pull/3208) ([dnielsen](https://github.com/dnielsen))
- update community information with updated links [\#3199](https://github.com/harness/drone/pull/3199) ([mrsantons](https://github.com/mrsantons))
## [v2.11.1](https://github.com/harness/drone/tree/v2.11.1) (2022-03-15)
[Full Changelog](https://github.com/harness/drone/compare/v2.11.0...v2.11.1)
**Fixed bugs:**
- ignore nil repos in list and add better debugging [\#3196](https://github.com/harness/drone/pull/3196) ([d1wilko](https://github.com/d1wilko))
**Merged pull requests:**
- \(maint\) release prep for 2.11.1 [\#3197](https://github.com/harness/drone/pull/3197) ([d1wilko](https://github.com/d1wilko))
## [v2.11.0](https://github.com/harness/drone/tree/v2.11.0) (2022-03-08)
[Full Changelog](https://github.com/harness/drone/compare/v2.10.0...v2.11.0)
**Implemented enhancements:**
- bump UI and SCM versions [\#3193](https://github.com/harness/drone/pull/3193) ([d1wilko](https://github.com/d1wilko))
**Merged pull requests:**
- \(maint\) release prep for 2.11.0 [\#3194](https://github.com/harness/drone/pull/3194) ([d1wilko](https://github.com/d1wilko))
## [v2.10.0](https://github.com/harness/drone/tree/v2.10.0) (2022-03-03)
[Full Changelog](https://github.com/harness/drone/compare/v2.9.1...v2.10.0)
**Implemented enhancements:**
- bump UI version to v2.7.0 [\#3190](https://github.com/harness/drone/pull/3190) ([d1wilko](https://github.com/d1wilko))
- bump UI version to v2.6.2 [\#3188](https://github.com/harness/drone/pull/3188) ([d1wilko](https://github.com/d1wilko))
**Merged pull requests:**
- \(maint\) release prep for 2.10.0 [\#3191](https://github.com/harness/drone/pull/3191) ([d1wilko](https://github.com/d1wilko))
## [v2.9.1](https://github.com/harness/drone/tree/v2.9.1) (2022-01-27)
[Full Changelog](https://github.com/harness/drone/compare/v2.9.0...v2.9.1)
**Fixed bugs:**
- bump ui version 2.6.1 [\#3185](https://github.com/harness/drone/pull/3185) ([d1wilko](https://github.com/d1wilko))
**Merged pull requests:**
- \(maint\) release prep for 2.9.1 [\#3186](https://github.com/harness/drone/pull/3186) ([tphoney](https://github.com/tphoney))
## [v2.9.0](https://github.com/harness/drone/tree/v2.9.0) (2022-01-26)
[Full Changelog](https://github.com/harness/drone/compare/v2.8.0...v2.9.0)
**Implemented enhancements:**
- bump ui to v2.6.0 [\#3183](https://github.com/harness/drone/pull/3183) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Merged pull requests:**
- release prep for v2.9.0 [\#3184](https://github.com/harness/drone/pull/3184) ([eoinmcafee00](https://github.com/eoinmcafee00))
## [v2.8.0](https://github.com/harness/drone/tree/v2.8.0) (2022-01-11)
[Full Changelog](https://github.com/harness/drone/compare/v2.7.3...v2.8.0)
**Implemented enhancements:**
- bump UI to v2.5.0 [\#3180](https://github.com/harness/drone/pull/3180) ([eoinmcafee00](https://github.com/eoinmcafee00))
- \(feat\) ignore archive repos on sync [\#3178](https://github.com/harness/drone/pull/3178) ([eoinmcafee00](https://github.com/eoinmcafee00))
- Datadog add the tag of 'remote:gitee' [\#3174](https://github.com/harness/drone/pull/3174) ([kit101](https://github.com/kit101))
- Add tag filter when call build list endpoint [\#3173](https://github.com/harness/drone/pull/3173) ([michelangelomo](https://github.com/michelangelomo))
**Fixed bugs:**
- \(maint\) add warning around typo for stage\_id in step struct [\#3179](https://github.com/harness/drone/pull/3179) ([tphoney](https://github.com/tphoney))
**Merged pull requests:**
- release prep v2.8.0 [\#3181](https://github.com/harness/drone/pull/3181) ([eoinmcafee00](https://github.com/eoinmcafee00))
## [v2.7.3](https://github.com/harness/drone/tree/v2.7.3) (2021-12-30)
[Full Changelog](https://github.com/harness/drone/compare/v2.7.2...v2.7.3)
**Fixed bugs:**
- bump go-scm to v1.16.3 [\#3175](https://github.com/harness/drone/pull/3175) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Merged pull requests:**
- release prep v2.7.3 [\#3176](https://github.com/harness/drone/pull/3176) ([eoinmcafee00](https://github.com/eoinmcafee00))
## [v2.7.2](https://github.com/harness/drone/tree/v2.7.2) (2021-12-19)
[Full Changelog](https://github.com/harness/drone/compare/v2.7.1...v2.7.2)
**Implemented enhancements:**
- bump go-scm to v1.16.2 [\#3169](https://github.com/harness/drone/pull/3169) ([kit101](https://github.com/kit101))
**Fixed bugs:**
- fixbug gitee provide refresher [\#3168](https://github.com/harness/drone/pull/3168) ([kit101](https://github.com/kit101))
**Merged pull requests:**
- release prep 2.7.2 [\#3172](https://github.com/harness/drone/pull/3172) ([eoinmcafee00](https://github.com/eoinmcafee00))
## [v2.7.1](https://github.com/harness/drone/tree/v2.7.1) (2021-12-17)
[Full Changelog](https://github.com/harness/drone/compare/v2.7.0...v2.7.1)
**Fixed bugs:**
- fixes issue with redirects on double slashes in url [\#3170](https://github.com/harness/drone/pull/3170) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Merged pull requests:**
- release prep v2.7.1 [\#3171](https://github.com/harness/drone/pull/3171) ([eoinmcafee00](https://github.com/eoinmcafee00))
## [v2.7.0](https://github.com/harness/drone/tree/v2.7.0) (2021-12-15)
[Full Changelog](https://github.com/harness/drone/compare/v2.6.0...v2.7.0)
**Implemented enhancements:**
- bump UI to v2.4.1 [\#3167](https://github.com/harness/drone/pull/3167) ([d1wilko](https://github.com/d1wilko))
**Fixed bugs:**
- \(DRON-157\) use deploy string in deployment [\#3165](https://github.com/harness/drone/pull/3165) ([tphoney](https://github.com/tphoney))
**Merged pull requests:**
- release v2.7.0 [\#3166](https://github.com/harness/drone/pull/3166) ([d1wilko](https://github.com/d1wilko))
## [v2.6.0](https://github.com/harness/drone/tree/v2.6.0) (2021-11-30)
[Full Changelog](https://github.com/harness/drone/compare/v2.5.0...v2.6.0)
**Implemented enhancements:**
- Feat: implemented gitee client [\#3156](https://github.com/harness/drone/pull/3156) ([kit101](https://github.com/kit101))
**Merged pull requests:**
- release prep for v2.6.0 [\#3163](https://github.com/harness/drone/pull/3163) ([tphoney](https://github.com/tphoney))
## [v2.5.0](https://github.com/harness/drone/tree/v2.5.0) (2021-11-17)
[Full Changelog](https://github.com/harness/drone/compare/v2.4.0...v2.5.0)
**Implemented enhancements:**
- bump ui to v2.4.0 [\#3160](https://github.com/harness/drone/pull/3160) ([eoinmcafee00](https://github.com/eoinmcafee00))
- add new endpoint for uploading cards [\#3159](https://github.com/harness/drone/pull/3159) ([eoinmcafee00](https://github.com/eoinmcafee00))
- refactor create / find / delete end points for cards [\#3158](https://github.com/harness/drone/pull/3158) ([eoinmcafee00](https://github.com/eoinmcafee00))
- bump ui to v2.3.1 [\#3155](https://github.com/harness/drone/pull/3155) ([d1wilko](https://github.com/d1wilko))
- provide ability to create/read/store card data in drone server [\#3149](https://github.com/harness/drone/pull/3149) ([eoinmcafee00](https://github.com/eoinmcafee00))
- \(DRON-124\) adding new status endpoint [\#3143](https://github.com/harness/drone/pull/3143) ([tphoney](https://github.com/tphoney))
**Fixed bugs:**
- fix a typo in readme [\#3150](https://github.com/harness/drone/pull/3150) ([nothatDinger](https://github.com/nothatDinger))
**Merged pull requests:**
- release prep for v2.5.0 [\#3161](https://github.com/harness/drone/pull/3161) ([eoinmcafee00](https://github.com/eoinmcafee00))
## [v2.4.0](https://github.com/harness/drone/tree/v2.4.0) (2021-09-23)
[Full Changelog](https://github.com/harness/drone/compare/v2.3.1...v2.4.0)
**Implemented enhancements:**
- bump ui version to v2.3.0 [\#3146](https://github.com/harness/drone/pull/3146) ([d1wilko](https://github.com/d1wilko))
- verify if the application is buildable [\#3144](https://github.com/harness/drone/pull/3144) ([marko-gacesa](https://github.com/marko-gacesa))
**Fixed bugs:**
- fixes build issue with bitbucket cloud [\#3147](https://github.com/harness/drone/pull/3147) ([eoinmcafee00](https://github.com/eoinmcafee00))
- Fix stepLimit param in Starlark and Template OSS code [\#3141](https://github.com/harness/drone/pull/3141) ([phil-davis](https://github.com/phil-davis))
- fix a broken link in readme [\#3140](https://github.com/harness/drone/pull/3140) ([empire](https://github.com/empire))
**Merged pull requests:**
- \(maint\)-release 2.4.0 [\#3148](https://github.com/harness/drone/pull/3148) ([d1wilko](https://github.com/d1wilko))
- Allow jsonnet imports in pipeline configuration [\#3105](https://github.com/harness/drone/pull/3105) ([hhamalai](https://github.com/hhamalai))
## [v2.3.1](https://github.com/harness/drone/tree/v2.3.1) (2021-09-09)
[Full Changelog](https://github.com/harness/drone/compare/v2.3.0...v2.3.1)
**Implemented enhancements:**
- bump ui to v2.2.1 - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3138](https://github.com/harness/drone/pull/3138) ([d1wilko](https://github.com/d1wilko))
**Merged pull requests:**
- \(maint\)-release 2.3.1 [\#3139](https://github.com/harness/drone/pull/3139) ([d1wilko](https://github.com/d1wilko))
## [v2.3.0](https://github.com/harness/drone/tree/v2.3.0) (2021-09-09)
[Full Changelog](https://github.com/harness/drone/compare/v2.2.0...v2.3.0)
**Implemented enhancements:**
- bump ui to v2.2.0 - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3137](https://github.com/harness/drone/pull/3137) ([d1wilko](https://github.com/d1wilko))
- Make starlark step limit configurable [\#3134](https://github.com/harness/drone/pull/3134) ([phil-davis](https://github.com/phil-davis))
- \(feat\) drone h/a: wrapped scheduler's signal func with redis mutex [\#3130](https://github.com/harness/drone/pull/3130) ([marko-gacesa](https://github.com/marko-gacesa))
**Fixed bugs:**
- \(fix\) trim http/s prefixes from config hostnames [\#3136](https://github.com/harness/drone/pull/3136) ([tphoney](https://github.com/tphoney))
- \(fix\) remove unused jwt-go library [\#3129](https://github.com/harness/drone/pull/3129) ([tphoney](https://github.com/tphoney))
## [v2.2.0](https://github.com/harness/drone/tree/v2.2.0) (2021-09-01)
[Full Changelog](https://github.com/harness/drone/compare/v2.1.0...v2.2.0)
**Implemented enhancements:**
- \(maint\) ui version v2.1.0 - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3132](https://github.com/harness/drone/pull/3132) ([d1wilko](https://github.com/d1wilko))
- Ability to cancel running builds, if a new commit is pushed [\#3126](https://github.com/harness/drone/pull/3126) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Fixed bugs:**
- fix templating reg expression to match if .drone.yml contains --- characters [\#3131](https://github.com/harness/drone/pull/3131) ([eoinmcafee00](https://github.com/eoinmcafee00))
- add check on template extension type - throw error if invalid [\#3128](https://github.com/harness/drone/pull/3128) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Merged pull requests:**
- \(maint\)-release 2.2.0 [\#3133](https://github.com/harness/drone/pull/3133) ([eoinmcafee00](https://github.com/eoinmcafee00))
- Rename files with camelCase name to use snake\_case convention [\#3127](https://github.com/harness/drone/pull/3127) ([marko-gacesa](https://github.com/marko-gacesa))
- event-stream supports timeout [\#3125](https://github.com/harness/drone/pull/3125) ([zc2638](https://github.com/zc2638))
- \(maint\) Readme update Add Contributor Section [\#3111](https://github.com/harness/drone/pull/3111) ([mrsantons](https://github.com/mrsantons))
## [v2.1.0](https://github.com/harness/drone/tree/v2.1.0) (2021-08-24)
[Full Changelog](https://github.com/harness/drone/compare/v2.0.6...v2.1.0)
**Implemented enhancements:**
- \(maint\) ui version v2.0.1. - https://github.com/drone/drone-ui/blob/main/CHANGELOG.md [\#3123](https://github.com/harness/drone/pull/3123) ([d1wilko](https://github.com/d1wilko))
- add support for yaml templates [\#3120](https://github.com/harness/drone/pull/3120) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Fixed bugs:**
- Update error message to forbidden if user membership doesn't exist on repo [\#3122](https://github.com/harness/drone/pull/3122) ([eoinmcafee00](https://github.com/eoinmcafee00))
- update create template path to have namespace, instead of inside the payload [\#3121](https://github.com/harness/drone/pull/3121) ([eoinmcafee00](https://github.com/eoinmcafee00))
- update dependency drone/go-scm to 1.15.2 to fix gitea build problem [\#3118](https://github.com/harness/drone/pull/3118) ([sesky4](https://github.com/sesky4))
**Merged pull requests:**
- \(maint\) v2.1.0 release prep [\#3124](https://github.com/harness/drone/pull/3124) ([d1wilko](https://github.com/d1wilko))
## [v2.0.6](https://github.com/harness/drone/tree/v2.0.6) (2021-08-17)
[Full Changelog](https://github.com/harness/drone/compare/v2.0.5...v2.0.6)
**Merged pull requests:**
- \(maint\) v2.0.6 release prep [\#3119](https://github.com/harness/drone/pull/3119) ([tphoney](https://github.com/tphoney))
## [v2.0.5](https://github.com/harness/drone/tree/v2.0.5) (2021-08-17)
[Full Changelog](https://github.com/harness/drone/compare/v2.0.4...v2.0.5)
**Implemented enhancements:**
- bump ui version [\#3115](https://github.com/harness/drone/pull/3115) ([d1wilko](https://github.com/d1wilko))
- bump ui version [\#3114](https://github.com/harness/drone/pull/3114) ([d1wilko](https://github.com/d1wilko))
- Add support for nested data objects within templates [\#3110](https://github.com/harness/drone/pull/3110) ([eoinmcafee00](https://github.com/eoinmcafee00))
- \(feat\) redis implementation for pub-sub, log streaming and canceller [\#3108](https://github.com/harness/drone/pull/3108) ([marko-gacesa](https://github.com/marko-gacesa))
**Fixed bugs:**
- fix issue where map changes order therefore test randomly fails [\#3112](https://github.com/harness/drone/pull/3112) ([eoinmcafee00](https://github.com/eoinmcafee00))
**Merged pull requests:**
- release 2.0.5 [\#3117](https://github.com/harness/drone/pull/3117) ([eoinmcafee00](https://github.com/eoinmcafee00))
- Update pull\_request\_template.md [\#3107](https://github.com/harness/drone/pull/3107) ([tphoney](https://github.com/tphoney))
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [2.0.4]
### Fixed
- DRON-97 remove use of request animation frame to prevent high CPU on tab refocus events.
## [2.0.3]
### Fixed
- DONE-91 handle extra slashes in url. [#3009](https://github.com/drone/drone/pull/3099).
## [2.0.2]
### Added
- Merge remote-tracking branch 'origin/master'
- prevent repository list short circuit in UI
- remove deprecated steps from building file [#3097](https://github.com/drone/drone/pull/3097)
- adding depends_on, image and detached fields to step [#3072](https://github.com/drone/drone/pull/3072)
- Add ctx.build.debug boolean [#3082](https://github.com/drone/drone/pull/3082)
- Bump github.com/google/go-jsonnet to v0.17.0 [#3084](https://github.com/drone/drone/pull/3084)
- bump go-scm v1.15.1 [#3096](https://github.com/drone/drone/pull/3096)
- bitbucket server build issue [#3092](https://github.com/drone/drone/pull/3092)
- update scm version [#3091](https://github.com/drone/drone/pull/3091)
- Limit graceful shutdown duration [#3093](https://github.com/drone/drone/pull/3093)
- bump user interface
- bump ui version
- ignore skip directive for promote and rollback events
- new feature: maximum open DB connections is configurable[#3089](https://github.com/drone/drone/pull/3089)
- jsonnet additional parameters [#3087](https://github.com/drone/drone/pull/3087)
- hide login button if user already authenticated
- new feature: configuration templates [#3081](https://github.com/drone/drone/pull/3081)
### Fixed
- various typos [#3088](https://github.com/drone/drone/pull/3088)
- handle error properly if template doesn't exist [#3095](https://github.com/drone/drone/pull/3093)
- oss build issue [#3086](https://github.com/drone/drone/pull/3086)
- graceful shutdown [#3083](https://github.com/drone/drone/pull/3083)
## [2.0.1]
### Added
- support for configuring the internal yaml cache size.
## [2.0.0]
### Added
- feature flags for mixed-mode database encryption.
### Changed
- user-interface re-design
### Breaking
- removed deprecated kubernetes integration in favor of official kubernetes runner.
- removed deprecated nomad integration in favor of official nomad runner.
## [1.10.1]
### Added
- support for repository-level concurrency limits.
- support for gitlab and github internal visibility on initial sync.
### Fixed
- create machine user with a custom API token.
## [1.10.0]
### Added
- support for starlark scripts in core.
- support for executing pipelines in debug mode.
## [1.9.2]
### Added
- update go-scm dependency to fix
## [1.9.1]
### Added
- support for increasing the http request timeout for extensions. [#2998](https://github.com/drone/drone/pull/2998).
- support for skipping a pipeline if the validation extension returns an ErrSkip.
- support for blocking a pipeline if the validation extension returns an ErrBlock.
### Fixed
- rollback endpoint should be available to users with write permission.
- retrying a build should re-use custom build parameters from parent build.
## [1.9.0] - 2020-07-12
### Added
- ui support for deployment list and summary.
- ui support for promoting and rolling back builds.
- feature flag to use static secret when signing webhooks, from @chiraggadasc.
### Fixed
- ui branch list improperly capped.
### Changed
- upgrade drone/envsubst dependency
- upgrade drone/go-scm dependency
## [1.8.1] - 2020-06-23
### Fixed
- support for gitea api pagination, repository sync hanging.
## [1.8.0] - 2020-06-10
### Added
- re-assigned repository ownership when deactivating a user.
- re-assigned repository ownership when deleting a user.
- de-activate a repository when deleting a user if re-assignment fails.
- de-activate a repository when deactivating a user if re-assignment fails.
- routine to cleanup builds stuck in a pending state.
- routine to cleanup builds stuck in a running state.
- private mode setting requires authentication to view public repositories.
### Fixed
- canceling a build emits a sql.ErrNoRows error.
- custom token is ignored when creating a user account via the API.
- machine accounts with sufficient permissions can create builds via the API.
### Changed
- upgraded Go toolchain to version 1.14.4.
## [1.7.0] - 2020-03-27
### Added
- endpoint to display the latest build by branch. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to display the latest build by pull request. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to display the latest build by environment. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to delete a branch from the index. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to delete a pull request from the index. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to delete an environment from the index. [#2940](https://github.com/drone/drone/pull/2940).
- page to view the latest build per branch.
### Fixed
- sync routine not executing asynchronously, being cancelled by http context.
- sync routine should ignore gitlab subrepositories
- convert deploy events in 0.8 yaml to promote events.
- do not execute cron job for disabled repositories. [#2931](https://github.com/drone/drone/issues/2931).
- remove trailing slash from gitea url to prevent oauth2 token refresh errors, by [@cmj0121](https://github.com/cmj0121). [#2920](https://github.com/drone/drone/issues/2920).
- disable font ligatures in build log output. [drone/drone-ui#322](https://github.com/drone/drone-ui/pull/322).
- missing am/pm in timestamps
## [1.6.5] - 2020-01-29
### Changed
- update version of go-scm
- update alpine version in docker images
- use ticker for cron jobs for more accurate timing
## [1.6.4] - 2019-12-30
### Added
- optionally enable pprof endpoints for profiling, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.6.3] - 2019-12-10
### Fixed
- disable caching generated yaml files by commit sha, by [@bradrydzewski](https://github.com/bradrydzewski).
### Added
- support for bitbucket skipverify, by [@toni-moreno](https://github.com/toni-moreno).
- support for gitea skipverify, by [@toni-moreno](https://github.com/toni-moreno).
## [1.6.2] - 2019-11-08
### Added
- support for loading license contents from env, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- regression not converting legacy pipeline when using new runners, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.6.1] - 2019-10-17
### Added
- updated autocert library in support of acme v2 protocol, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- fixed nil pointer when manually adding user from api, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.6.0] - 2019-10-04
### Added
- added nsswitch to docker images
- option to auto-cancel pending builds when newer build enqueued, by [@bradrydzewski](https://github.com/bradrydzewski). [#1980](https://github.com/drone/drone/issues/1980).
- endpoint to list all repositories in the database, by [@bradrydzewski](https://github.com/bradrydzewski). [#2785](https://github.com/drone/drone/issues/2785).
### Fixed
- improve sync to handle duplicate repository names with different unique identifiers, by [@bradrydzewski](https://github.com/bradrydzewski). [#2658](https://github.com/drone/drone/issues/2658). _You can revert to the previous sync logic with DRONE_DATABASE_LEGACY_BATCH=true_.
## [1.5.1] - 2019-09-30
### Added
- allow organization admins access to organization secret endpoints, by [@bradrydzewski](https://github.com/bradrydzewski). [#2838](https://github.com/drone/drone/issues/2838).
### Fixed
- fix invalid deep links in UI for github enterprise, by [@bradrydzewski](https://github.com/bradrydzewski).
- ensure correct casing when manually adding user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2766](https://github.com/drone/drone/issues/2766).
## [1.5.0] - 2019-09-28
### Added
- endpoint to execute a cron pipeline on-demand, by [@bradrydzewski](https://github.com/bradrydzewski). [#2781](https://github.com/drone/drone/issues/2781).
- endpoint to list builds by branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#1495](https://github.com/drone/drone/issues/1495).
- ignore skip comments when cron event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2835](https://github.com/drone/drone/issues/2835).
- support for admission extensions, by [@bradrydzewski](https://github.com/bradrydzewski). [#2043](https://github.com/drone/drone/issues/2043).
- endpoint to provide link to git resources, by [@bradrydzewski](https://github.com/bradrydzewski). [#2843](https://github.com/drone/drone/issues/2843).
- improve bitbucket status display text on new pull request screen, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- missing cron job name in user interface, by [@bradrydzewski](https://github.com/bradrydzewski).
- log lines not properly wrapping in user interface, by [@bradrydzewski](https://github.com/bradrydzewski).
[#309](https://github.com/drone/drone-ui/issues/309).
### Breaking
- the server now runs in multi-machine mode by default. In order to run the server in single-machine mode (agents disabled) you must set DRONE_AGENTS_DISABLED=true.
## [1.4.0] - 2019-09-12
### Added
- upgrade to Go 1.13 to resolve arm segfault, by [@KN4CK3R](https://github.com/KN4CK3R). [#2823](https://github.com/drone/drone/issues/2823).
- configure default visibility, by [@JordanSussman](https://github.com/JordanSussman). [#2824](https://github.com/drone/drone/issues/2824).
- configure default trusted flag, by [@vyckou](https://github.com/vyckou).
- support for validation plugins, by [@bradrydzewski](https://github.com/bradrydzewski). [#2266](https://github.com/drone/drone/issues/2266).
- support for conversion plugins, by [@bradrydzewski](https://github.com/bradrydzewski).
- support for cron event type, by [@bradrydzewski](https://github.com/bradrydzewski). [#2705](https://github.com/drone/drone/issues/2705).
- support for rollback event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2695](https://github.com/drone/drone/issues/2695).
- support for lets encrypt email, by [@bradrydzewski](https://github.com/bradrydzewski). [#2505](https://github.com/drone/drone/issues/2505).
### Removed
- Support for basic auth as an option for Gitea, by [@techknowlogick](https://giteahub.com/techknowlogick). [#2721](https://github.com/drone/drone/issues/2721)
### Fixed
- copy cron job name when restarting a cron job, by [@bradrydzewski](https://github.com/bradrydzewski). [#2760](https://github.com/drone/drone/issues/2760).
## [1.3.1] - 2019-08-26
### Added
- support for the GitHub deployment status API, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.3.0] - 2019-08-20
### Added
- support for storing logs in Azure Cloud Storage, by [@Lucretius](https://github.com/Lucretius). [#2788](https://github.com/drone/drone/pull/2788)
- support for windows server 1903, by [@bradrydzewski](https://github.com/bradrydzewski).
- button to view the full log file, by [@dramich](https://github.com/dramich). [drone/drone-ui#287](https://github.com/drone/drone-ui/pull/287).
### Fixed
- read gogs sha from webhook, by [@marcotuna](https://github.com/marcotuna).
- create bind volume on host if not exists, by [@bradrydzewski](https://github.com/bradrydzewski). [#2725](https://github.com/drone/drone/issues/2725).
- preserve whitespace in build logs, by [@geek1011](https://github.com/geek1011). [drone/drone-ui#294](https://github.com/drone/drone-ui/pull/294).
- enable log file download on firefox, by [@bobmanary](https://github.com/bobmanary). [drone/drone-ui#303](https://github.com/drone/drone-ui/pull/303)
### Security
- upgraded to Go 1.12.9 due to CVE-2019-9512 and CVE-2019-9514
## [1.2.3] - 2019-07-30
### Added
- disable github status for cron jobs
- support for action in conditionals, by [@bradrydzewski](https://github.com/bradrydzewski). [#2685](https://github.com/drone/drone/issues/2685).
### Fixed
- improve cancel logic for dangling stages, by [@bradrydzewski](https://github.com/bradrydzewski).
- improve error when kubernetes malforms the port configuration, by [@bradrydzewski](https://github.com/bradrydzewski). [#2742](https://github.com/drone/drone/issues/2742).
- copy parameters from parent build when promoting, by [@bradrydzewski](https://github.com/bradrydzewski). [#2748](https://github.com/drone/drone/issues/2748).
## [1.2.2] - 2019-07-29
### Added
- support for legacy environment variables
- support for legacy workspace based on repository name
- support for github deployment hooks
- provide base sha for github pull requests
- option to filter webhooks by event and type
- upgrade drone-yaml to v1.2.2
- upgrade drone-runtime to v1.0.7
### Fixed
- error when manually creating an empty user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2738](https://github.com/drone/drone/issues/2738).
## [1.2.1] - 2019-06-11
### Added
- support for legacy tokens to ease upgrade path, by [@bradrydzewski](https://github.com/bradrydzewski). [#2713](https://github.com/drone/drone/issues/2713).
- include repository name and id in batch update error message, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- fix inconsistent base64 encoding and decoding of encrypted secrets, by [@bradrydzewski](https://github.com/bradrydzewski).
- update drone-yaml to version 1.1.2 for improved 0.8 to 1.0 yaml marshal escaping.
- update drone-yaml to version 1.1.3 for improved 0.8 to 1.0 workspace conversion.
## [1.2.0] - 2019-05-30
### Added
- endpoint to trigger new build for default branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- endpoint to trigger new build for branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- endpoint to trigger new build for branch and sha, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- enable optional prometheus metrics guest access, by [@janberktold](https://github.com/janberktold)
- fallback to database when logs not found in s3, by [@bradrydzewski](https://github.com/bradrydzewski). [#2689](https://github.com/drone/drone/issues/2689).
- support for custom stage definitions and runners, by [@bradrydzewski](https://github.com/bradrydzewski). [#2680](https://github.com/drone/drone/issues/2680).
- update drone-yaml to version 1.1.0
### Fixed
- retrieve latest build by branch, by [@tboerger](https://github.com/tboerger).
- copy the fork value when restarting a build, by [@bradrydzewski](https://github.com/bradrydzewski). [#2708](https://github.com/drone/drone/issues/2708).
- make healthz available without redirect, by [@bradrydzewski](https://github.com/bradrydzewski). [#2706](https://github.com/drone/drone/issues/2706).
## [1.1.0] - 2019-04-23
### Added
- specify a user for the pipeline step, by [@bradrydzewski](https://github.com/bradrydzewski). [#2651](https://github.com/drone/drone/issues/2651).
- support for Gitea oauth2, by [@techknowlogick](https://github.com/techknowlogick). [#2622](https://github.com/drone/drone/pull/2622).
- ping the docker daemon before starting the agent, by [@bradrydzewski](https://github.com/bradrydzewski). [#2495](https://github.com/drone/drone/issues/2495).
- support for Cron job name in Yaml trigger block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628).
- support for Cron job name in Yaml when block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628).
- sqlite username column changed to case-insensitive, by [@bradrydzewski](https://github.com/bradrydzewski).
- endpoint to purge repository from database, by [@bradrydzewski](https://github.com/bradrydzewski).
- support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski).
- include system metadata in global webhooks, by [@bradrydzewski](https://github.com/bradrydzewski).
- ability to customize cookie secure flag, by [@bradrydzewski](https://github.com/bradrydzewski).
- update drone-yaml from version 1.0.6 to 1.0.8.
- update drone-runtime from version 1.0.4 to 1.0.6.
- update go-scm from version 1.0.3 to 1.0.4.
### Fixed
- fixed error in mysql table creation syntax, from [@xuyang2](https://github.com/xuyang2). [#2677](https://github.com/drone/drone/pull/2677).
- fixed stuck builds when upstream dependency is skipped, from [@bradrydzewski](https://github.com/bradrydzewski). [#2634](https://github.com/drone/drone/issues/2634).
- fixed issue running steps with dependencies on failure, from [@bradrydzewski](https://github.com/bradrydzewski). [#2667](https://github.com/drone/drone/issues/2667).
## [1.0.1] - 2019-04-10
### Added
- pass stage environment variables to pipeline steps, by [@bradrydzewski](https://github.com/bradrydzewski).
- update go-scm to version 1.3.0, by [@bradrydzewski](https://github.com/bradrydzewski).
- update drone-runtime to version to 1.0.4, by [@bradrydzewski](https://github.com/bradrydzewski).
- ping docker daemon before agent starts to ensure connectivity, by [@bradrydzewski](https://github.com/bradrydzewski).
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*

View File

@ -1,69 +0,0 @@
# Contributing to Harness
Thank you for your interest in open source contributions to Harness. Harness uses GitHub to manage open source reviews of pull requests.
* If you are a new contributor see: [Steps to Contribute](#steps-to-contribute)
* If you have a minor fix or improvement, feel free to create a pull request. Please provide necessary details in the pull request description and use a meaningful title.
* If you plan to do something more involved, first discuss your ideas by [raising an issue](https://github.com/harness/harness/issues). This will avoid unnecessary work and surely give you and us a good deal of inspiration.
* Relevant coding style guidelines are
- For backend: the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the formatting and style section of Peter Bourgon's [Go: Best Practices for Production Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style)
- For frontend: [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html) and [Best practices for Typescript coding](https://medium.com/@eshagarg1996/best-practices-for-typescript-coding-8b1ea98d02f8).
* Be sure to sign off on the [CLA](https://cla-assistant.io/harness/gitness).
## Steps to Contribute
Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on. This is to prevent duplicated efforts from contributors on the same issue.
Please check the [`good-first-issue`](https://github.com/harness/harness/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label to find issues that are good for getting started. If you have questions about one of the issues, with or without the tag, please comment on them and one of the maintainers will clarify it. For a quicker response, contact us over [slack](https://developer.harness.io/docs/open-source/support#slack).
### Local Development
Please review [Harness development](https://github.com/harness/harness/tree/main?tab=readme-ov-file#harness-development) to build and test your code locally.
### Pre-commit Hook
We have a pre-commit hook to ensure code quality before committing changes. This hook checks for required binaries (grep, sed, and xargs) and runs checks specifically for Go files (*.go). If any issues are found during the checks, the commit process will be halted until the issues are resolved.
### Lint Check
Our CI Linter pipeline conducts automated checks for code quality, with [separate lint checks for Go and TypeScript](https://github.com/harness/harness/blob/main/.github/workflows/ci-lint.yml). These checks help ensure adherence to coding standards and identify potential issues early in the development process. Thank you for contributing to our code quality efforts!
## Pull Request Checklist
* Branch from the main branch and, if needed, rebase to the current main branch before submitting your pull request. If it doesn't merge cleanly with main you may be asked to rebase your changes.
* Commits should be as small as possible, while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests).
* If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment.
* Add tests relevant to the fixed bug or new feature.
## Dependency management
Harness uses [Go modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) to manage dependencies on external packages.
To add or update a new dependency, use the `go get` command:
```bash
# Pick the latest tagged release.
go get example.com/some/module/pkg@latest
# Pick a specific version.
go get example.com/some/module/pkg@vX.Y.Z
```
Tidy up the `go.mod` and `go.sum` files:
```bash
# The GO111MODULE variable can be omitted when the code isn't located in GOPATH.
GO111MODULE=on go mod tidy
```
You have to commit the changes to `go.mod` and `go.sum` before submitting the pull request.

View File

@ -1,96 +0,0 @@
# ---------------------------------------------------------#
# Build web image #
# ---------------------------------------------------------#
FROM --platform=$BUILDPLATFORM node:16 as web
WORKDIR /usr/src/app
COPY web/package.json ./
COPY web/yarn.lock ./
# If you are building your code for production
# RUN npm ci --omit=dev
COPY ./web .
RUN yarn && yarn build && yarn cache clean
# ---------------------------------------------------------#
# Build Harness image #
# ---------------------------------------------------------#
FROM --platform=$BUILDPLATFORM golang:1.24.9-alpine3.22 as builder
RUN apk update \
&& apk add --no-cache protoc build-base git
# Setup workig dir
WORKDIR /app
RUN git config --global --add safe.directory '/app'
# Get dependencies - will also be cached if we won't change mod/sum
COPY go.mod .
COPY go.sum .
COPY Makefile .
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN make dep
RUN make tools
# COPY the source code as the last step
COPY . .
COPY --from=web /usr/src/app/dist /app/web/dist
# build
ARG GIT_COMMIT
ARG GITNESS_VERSION_MAJOR
ARG GITNESS_VERSION_MINOR
ARG GITNESS_VERSION_PATCH
ARG TARGETOS TARGETARCH
RUN if [ "$TARGETARCH" = "arm64" ]; then \
wget -P ~ https://musl.cc/aarch64-linux-musl-cross.tgz && \
tar -xvf ~/aarch64-linux-musl-cross.tgz -C ~ ; \
fi
# set required build flags
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
if [ "$TARGETARCH" = "arm64" ]; then CC=~/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc; fi && \
LDFLAGS="-X github.com/harness/gitness/version.GitCommit=${GIT_COMMIT} -X github.com/harness/gitness/version.major=${GITNESS_VERSION_MAJOR} -X github.com/harness/gitness/version.minor=${GITNESS_VERSION_MINOR} -X github.com/harness/gitness/version.patch=${GITNESS_VERSION_PATCH} -extldflags '-static'" && \
CGO_ENABLED=1 \
GOOS=$TARGETOS GOARCH=$TARGETARCH \
CC=$CC go build -ldflags="$LDFLAGS" -o ./gitness ./cmd/gitness
### Pull CA Certs
FROM --platform=$BUILDPLATFORM alpine:latest as cert-image
RUN apk --update add ca-certificates
# ---------------------------------------------------------#
# Create final image #
# ---------------------------------------------------------#
FROM --platform=$TARGETPLATFORM alpine/git:2.49.1 as final
# setup app dir and its content
WORKDIR /app
VOLUME /data
ENV XDG_CACHE_HOME /data
ENV GITNESS_GIT_ROOT /data
ENV GITNESS_REGISTRY_FILESYSTEM_ROOT_DIRECTORY /data/registry
ENV GITNESS_DATABASE_DRIVER sqlite3
ENV GITNESS_DATABASE_DATASOURCE /data/database.sqlite
ENV GITNESS_METRIC_ENABLED=true
ENV GITNESS_METRIC_ENDPOINT=https://stats.drone.ci/api/v1/gitness
ENV GITNESS_TOKEN_COOKIE_NAME=token
ENV GITNESS_DOCKER_API_VERSION 1.41
ENV GITNESS_SSH_ENABLE=true
ENV GITNESS_GITSPACE_ENABLE=true
COPY --from=builder /app/gitness /app/gitness
COPY --from=cert-image /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
EXPOSE 3000
EXPOSE 3022
ENTRYPOINT [ "/app/gitness", "server" ]

View File

@ -1,86 +0,0 @@
# ---------------------------------------------------------#
# Pull UIv2 image #
# ---------------------------------------------------------#
# opensource-ui is only pushed as linux/arm64 - okay since we only copy files.
FROM --platform=linux/arm64 harness/opensource-ui:standalone.alpha.480 as uiv2
# ---------------------------------------------------------#
# Build Harness image #
# ---------------------------------------------------------#
FROM --platform=$BUILDPLATFORM golang:1.24.9-alpine3.22 as builder
RUN apk update \
&& apk add --no-cache protoc build-base git
# Setup workig dir
WORKDIR /app
RUN git config --global --add safe.directory '/app'
# Get dependencies - will also be cached if we won't change mod/sum
COPY go.mod .
COPY go.sum .
COPY Makefile .
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN make dep
RUN make tools
# COPY the source code as the last step
COPY . .
COPY --from=uiv2 /canary-dist /app/web/dist
# build
ARG GIT_COMMIT
ARG GITNESS_VERSION_MAJOR
ARG GITNESS_VERSION_MINOR
ARG GITNESS_VERSION_PATCH
ARG TARGETOS TARGETARCH
RUN if [ "$TARGETARCH" = "arm64" ]; then \
wget -P ~ https://musl.cc/aarch64-linux-musl-cross.tgz && \
tar -xvf ~/aarch64-linux-musl-cross.tgz -C ~ ; \
fi
# set required build flags
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
if [ "$TARGETARCH" = "arm64" ]; then CC=~/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc; fi && \
LDFLAGS="-X github.com/harness/gitness/version.GitCommit=${GIT_COMMIT} -X github.com/harness/gitness/version.major=${GITNESS_VERSION_MAJOR} -X github.com/harness/gitness/version.minor=${GITNESS_VERSION_MINOR} -X github.com/harness/gitness/version.patch=${GITNESS_VERSION_PATCH} -extldflags '-static'" && \
CGO_ENABLED=1 \
GOOS=$TARGETOS GOARCH=$TARGETARCH \
CC=$CC go build -ldflags="$LDFLAGS" -o ./gitness ./cmd/gitness
### Pull CA Certs
FROM --platform=$BUILDPLATFORM alpine:latest as cert-image
RUN apk --update add ca-certificates
# ---------------------------------------------------------#
# Create final image #
# ---------------------------------------------------------#
FROM --platform=$TARGETPLATFORM alpine/git:2.49.1 as final
# setup app dir and its content
WORKDIR /app
VOLUME /data
ENV XDG_CACHE_HOME /data
ENV GITNESS_GIT_ROOT /data
ENV GITNESS_REGISTRY_FILESYSTEM_ROOT_DIRECTORY /data/registry
ENV GITNESS_DATABASE_DRIVER sqlite3
ENV GITNESS_DATABASE_DATASOURCE /data/database.sqlite
ENV GITNESS_METRIC_ENABLED=true
ENV GITNESS_METRIC_ENDPOINT=https://stats.drone.ci/api/v1/gitness
ENV GITNESS_TOKEN_COOKIE_NAME=token
ENV GITNESS_DOCKER_API_VERSION 1.41
ENV GITNESS_SSH_ENABLE=true
ENV GITNESS_GITSPACE_ENABLE=true
COPY --from=builder /app/gitness /app/gitness
COPY --from=cert-image /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
EXPOSE 3000
EXPOSE 3022
ENTRYPOINT [ "/app/gitness", "server" ]

326
HISTORY.md Normal file
View File

@ -0,0 +1,326 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [2.0.4]
### Fixed
- DRON-97 remove use of request animation frame to prevent high CPU on tab refocus events.
## [2.0.3]
### Fixed
- DONE-91 handle extra slashes in url. [#3009](https://github.com/drone/drone/pull/3099).
## [2.0.2]
### Added
- Merge remote-tracking branch 'origin/master'
- prevent repository list short circuit in UI
- remove deprecated steps from building file [#3097](https://github.com/drone/drone/pull/3097)
- adding depends_on, image and detached fields to step [#3072](https://github.com/drone/drone/pull/3072)
- Add ctx.build.debug boolean [#3082](https://github.com/drone/drone/pull/3082)
- Bump github.com/google/go-jsonnet to v0.17.0 [#3084](https://github.com/drone/drone/pull/3084)
- bump go-scm v1.15.1 [#3096](https://github.com/drone/drone/pull/3096)
- bitbucket server build issue [#3092](https://github.com/drone/drone/pull/3092)
- update scm version [#3091](https://github.com/drone/drone/pull/3091)
- Limit graceful shutdown duration [#3093](https://github.com/drone/drone/pull/3093)
- bump user interface
- bump ui version
- ignore skip directive for promote and rollback events
- new feature: maximum open DB connections is configurable[#3089](https://github.com/drone/drone/pull/3089)
- jsonnet additional parameters [#3087](https://github.com/drone/drone/pull/3087)
- hide login button if user already authenticated
- new feature: configuration templates [#3081](https://github.com/drone/drone/pull/3081)
### Fixed
- various typos [#3088](https://github.com/drone/drone/pull/3088)
- handle error properly if template doesn't exist [#3095](https://github.com/drone/drone/pull/3093)
- oss build issue [#3086](https://github.com/drone/drone/pull/3086)
- graceful shutdown [#3083](https://github.com/drone/drone/pull/3083)
## [2.0.1]
### Added
- support for configuring the internal yaml cache size.
## [2.0.0]
### Added
- feature flags for mixed-mode database encryption.
### Changed
- user-interface re-design
### Breaking
- removed deprecated kubernetes integration in favor of official kubernetes runner.
- removed deprecated nomad integration in favor of official nomad runner.
## [1.10.1]
### Added
- support for repository-level concurrency limits.
- support for gitlab and github internal visibility on initial sync.
### Fixed
- create machine user with a custom API token.
## [1.10.0]
### Added
- support for starlark scripts in core.
- support for executing pipelines in debug mode.
## [1.9.2]
### Added
- update go-scm dependency to fix
## [1.9.1]
### Added
- support for increasing the http request timeout for extensions. [#2998](https://github.com/drone/drone/pull/2998).
- support for skipping a pipeline if the validation extension returns an ErrSkip.
- support for blocking a pipeline if the validation extension returns an ErrBlock.
### Fixed
- rollback endpoint should be available to users with write permission.
- retrying a build should re-use custom build parameters from parent build.
## [1.9.0] - 2020-07-12
### Added
- ui support for deployment list and summary.
- ui support for promoting and rolling back builds.
- feature flag to use static secret when signing webhooks, from @chiraggadasc.
### Fixed
- ui branch list improperly capped.
### Changed
- upgrade drone/envsubst dependency
- upgrade drone/go-scm dependency
## [1.8.1] - 2020-06-23
### Fixed
- support for gitea api pagination, repository sync hanging.
## [1.8.0] - 2020-06-10
### Added
- re-assigned repository ownership when deactivating a user.
- re-assigned repository ownership when deleting a user.
- de-activate a repository when deleting a user if re-assignment fails.
- de-activate a repository when deactivating a user if re-assignment fails.
- routine to cleanup builds stuck in a pending state.
- routine to cleanup builds stuck in a running state.
- private mode setting requires authentication to view public repositories.
### Fixed
- canceling a build emits a sql.ErrNoRows error.
- custom token is ignored when creating a user account via the API.
- machine accounts with sufficient permissions can create builds via the API.
### Changed
- upgraded Go toolchain to version 1.14.4.
## [1.7.0] - 2020-03-27
### Added
- endpoint to display the latest build by branch. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to display the latest build by pull request. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to display the latest build by environment. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to delete a branch from the index. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to delete a pull request from the index. [#2940](https://github.com/drone/drone/pull/2940).
- endpoint to delete an environment from the index. [#2940](https://github.com/drone/drone/pull/2940).
- page to view the latest build per branch.
### Fixed
- sync routine not executing asynchronously, being cancelled by http context.
- sync routine should ignore gitlab subrepositories
- convert deploy events in 0.8 yaml to promote events.
- do not execute cron job for disabled repositories. [#2931](https://github.com/drone/drone/issues/2931).
- remove trailing slash from gitea url to prevent oauth2 token refresh errors, by [@cmj0121](https://github.com/cmj0121). [#2920](https://github.com/drone/drone/issues/2920).
- disable font ligatures in build log output. [drone/drone-ui#322](https://github.com/drone/drone-ui/pull/322).
- missing am/pm in timestamps
## [1.6.5] - 2020-01-29
### Changed
- update version of go-scm
- update alpine version in docker images
- use ticker for cron jobs for more accurate timing
## [1.6.4] - 2019-12-30
### Added
- optionally enable pprof endpoints for profiling, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.6.3] - 2019-12-10
### Fixed
- disable caching generated yaml files by commit sha, by [@bradrydzewski](https://github.com/bradrydzewski).
### Added
- support for bitbucket skipverify, by [@toni-moreno](https://github.com/toni-moreno).
- support for gitea skipverify, by [@toni-moreno](https://github.com/toni-moreno).
## [1.6.2] - 2019-11-08
### Added
- support for loading license contents from env, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- regression not converting legacy pipeline when using new runners, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.6.1] - 2019-10-17
### Added
- updated autocert library in support of acme v2 protocol, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- fixed nil pointer when manually adding user from api, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.6.0] - 2019-10-04
### Added
- added nsswitch to docker images
- option to auto-cancel pending builds when newer build enqueued, by [@bradrydzewski](https://github.com/bradrydzewski). [#1980](https://github.com/drone/drone/issues/1980).
- endpoint to list all repositories in the database, by [@bradrydzewski](https://github.com/bradrydzewski). [#2785](https://github.com/drone/drone/issues/2785).
### Fixed
- improve sync to handle duplicate repository names with different unique identifiers, by [@bradrydzewski](https://github.com/bradrydzewski). [#2658](https://github.com/drone/drone/issues/2658). _You can revert to the previous sync logic with DRONE_DATABASE_LEGACY_BATCH=true_.
## [1.5.1] - 2019-09-30
### Added
- allow organization admins access to organization secret endpoints, by [@bradrydzewski](https://github.com/bradrydzewski). [#2838](https://github.com/drone/drone/issues/2838).
### Fixed
- fix invalid deep links in UI for github enterprise, by [@bradrydzewski](https://github.com/bradrydzewski).
- ensure correct casing when manually adding user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2766](https://github.com/drone/drone/issues/2766).
## [1.5.0] - 2019-09-28
### Added
- endpoint to execute a cron pipeline on-demand, by [@bradrydzewski](https://github.com/bradrydzewski). [#2781](https://github.com/drone/drone/issues/2781).
- endpoint to list builds by branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#1495](https://github.com/drone/drone/issues/1495).
- ignore skip comments when cron event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2835](https://github.com/drone/drone/issues/2835).
- support for admission extensions, by [@bradrydzewski](https://github.com/bradrydzewski). [#2043](https://github.com/drone/drone/issues/2043).
- endpoint to provide link to git resources, by [@bradrydzewski](https://github.com/bradrydzewski). [#2843](https://github.com/drone/drone/issues/2843).
- improve bitbucket status display text on new pull request screen, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- missing cron job name in user interface, by [@bradrydzewski](https://github.com/bradrydzewski).
- log lines not properly wrapping in user interface, by [@bradrydzewski](https://github.com/bradrydzewski).
[#309](https://github.com/drone/drone-ui/issues/309).
### Breaking
- the server now runs in multi-machine mode by default. In order to run the server in single-machine mode (agents disabled) you must set DRONE_AGENTS_DISABLED=true.
## [1.4.0] - 2019-09-12
### Added
- upgrade to Go 1.13 to resolve arm segfault, by [@KN4CK3R](https://github.com/KN4CK3R). [#2823](https://github.com/drone/drone/issues/2823).
- configure default visibility, by [@JordanSussman](https://github.com/JordanSussman). [#2824](https://github.com/drone/drone/issues/2824).
- configure default trusted flag, by [@vyckou](https://github.com/vyckou).
- support for validation plugins, by [@bradrydzewski](https://github.com/bradrydzewski). [#2266](https://github.com/drone/drone/issues/2266).
- support for conversion plugins, by [@bradrydzewski](https://github.com/bradrydzewski).
- support for cron event type, by [@bradrydzewski](https://github.com/bradrydzewski). [#2705](https://github.com/drone/drone/issues/2705).
- support for rollback event, by [@bradrydzewski](https://github.com/bradrydzewski). [#2695](https://github.com/drone/drone/issues/2695).
- support for lets encrypt email, by [@bradrydzewski](https://github.com/bradrydzewski). [#2505](https://github.com/drone/drone/issues/2505).
### Removed
- Support for basic auth as an option for Gitea, by [@techknowlogick](https://giteahub.com/techknowlogick). [#2721](https://github.com/drone/drone/issues/2721)
### Fixed
- copy cron job name when restarting a cron job, by [@bradrydzewski](https://github.com/bradrydzewski). [#2760](https://github.com/drone/drone/issues/2760).
## [1.3.1] - 2019-08-26
### Added
- support for the GitHub deployment status API, by [@bradrydzewski](https://github.com/bradrydzewski).
## [1.3.0] - 2019-08-20
### Added
- support for storing logs in Azure Cloud Storage, by [@Lucretius](https://github.com/Lucretius). [#2788](https://github.com/drone/drone/pull/2788)
- support for windows server 1903, by [@bradrydzewski](https://github.com/bradrydzewski).
- button to view the full log file, by [@dramich](https://github.com/dramich). [drone/drone-ui#287](https://github.com/drone/drone-ui/pull/287).
### Fixed
- read gogs sha from webhook, by [@marcotuna](https://github.com/marcotuna).
- create bind volume on host if not exists, by [@bradrydzewski](https://github.com/bradrydzewski). [#2725](https://github.com/drone/drone/issues/2725).
- preserve whitespace in build logs, by [@geek1011](https://github.com/geek1011). [drone/drone-ui#294](https://github.com/drone/drone-ui/pull/294).
- enable log file download on firefox, by [@bobmanary](https://github.com/bobmanary). [drone/drone-ui#303](https://github.com/drone/drone-ui/pull/303)
### Security
- upgraded to Go 1.12.9 due to CVE-2019-9512 and CVE-2019-9514
## [1.2.3] - 2019-07-30
### Added
- disable github status for cron jobs
- support for action in conditionals, by [@bradrydzewski](https://github.com/bradrydzewski). [#2685](https://github.com/drone/drone/issues/2685).
### Fixed
- improve cancel logic for dangling stages, by [@bradrydzewski](https://github.com/bradrydzewski).
- improve error when kubernetes malforms the port configuration, by [@bradrydzewski](https://github.com/bradrydzewski). [#2742](https://github.com/drone/drone/issues/2742).
- copy parameters from parent build when promoting, by [@bradrydzewski](https://github.com/bradrydzewski). [#2748](https://github.com/drone/drone/issues/2748).
## [1.2.2] - 2019-07-29
### Added
- support for legacy environment variables
- support for legacy workspace based on repository name
- support for github deployment hooks
- provide base sha for github pull requests
- option to filter webhooks by event and type
- upgrade drone-yaml to v1.2.2
- upgrade drone-runtime to v1.0.7
### Fixed
- error when manually creating an empty user, by [@bradrydzewski](https://github.com/bradrydzewski). [#2738](https://github.com/drone/drone/issues/2738).
## [1.2.1] - 2019-06-11
### Added
- support for legacy tokens to ease upgrade path, by [@bradrydzewski](https://github.com/bradrydzewski). [#2713](https://github.com/drone/drone/issues/2713).
- include repository name and id in batch update error message, by [@bradrydzewski](https://github.com/bradrydzewski).
### Fixed
- fix inconsistent base64 encoding and decoding of encrypted secrets, by [@bradrydzewski](https://github.com/bradrydzewski).
- update drone-yaml to version 1.1.2 for improved 0.8 to 1.0 yaml marshal escaping.
- update drone-yaml to version 1.1.3 for improved 0.8 to 1.0 workspace conversion.
## [1.2.0] - 2019-05-30
### Added
- endpoint to trigger new build for default branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- endpoint to trigger new build for branch, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- endpoint to trigger new build for branch and sha, by [@bradrydzewski](https://github.com/bradrydzewski). [#2679](https://github.com/drone/drone/issues/2679).
- enable optional prometheus metrics guest access, by [@janberktold](https://github.com/janberktold)
- fallback to database when logs not found in s3, by [@bradrydzewski](https://github.com/bradrydzewski). [#2689](https://github.com/drone/drone/issues/2689).
- support for custom stage definitions and runners, by [@bradrydzewski](https://github.com/bradrydzewski). [#2680](https://github.com/drone/drone/issues/2680).
- update drone-yaml to version 1.1.0
### Fixed
- retrieve latest build by branch, by [@tboerger](https://github.com/tboerger).
- copy the fork value when restarting a build, by [@bradrydzewski](https://github.com/bradrydzewski). [#2708](https://github.com/drone/drone/issues/2708).
- make healthz available without redirect, by [@bradrydzewski](https://github.com/bradrydzewski). [#2706](https://github.com/drone/drone/issues/2706).
## [1.1.0] - 2019-04-23
### Added
- specify a user for the pipeline step, by [@bradrydzewski](https://github.com/bradrydzewski). [#2651](https://github.com/drone/drone/issues/2651).
- support for Gitea oauth2, by [@techknowlogick](https://github.com/techknowlogick). [#2622](https://github.com/drone/drone/pull/2622).
- ping the docker daemon before starting the agent, by [@bradrydzewski](https://github.com/bradrydzewski). [#2495](https://github.com/drone/drone/issues/2495).
- support for Cron job name in Yaml trigger block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628).
- support for Cron job name in Yaml when block, by [@bradrydzewski](https://github.com/bradrydzewski). [#2628](https://github.com/drone/drone/issues/2628).
- sqlite username column changed to case-insensitive, by [@bradrydzewski](https://github.com/bradrydzewski).
- endpoint to purge repository from database, by [@bradrydzewski](https://github.com/bradrydzewski).
- support for per-organization secrets, by [@bradrydzewski](https://github.com/bradrydzewski).
- include system metadata in global webhooks, by [@bradrydzewski](https://github.com/bradrydzewski).
- ability to customize cookie secure flag, by [@bradrydzewski](https://github.com/bradrydzewski).
- update drone-yaml from version 1.0.6 to 1.0.8.
- update drone-runtime from version 1.0.4 to 1.0.6.
- update go-scm from version 1.0.3 to 1.0.4.
### Fixed
- fixed error in mysql table creation syntax, from [@xuyang2](https://github.com/xuyang2). [#2677](https://github.com/drone/drone/pull/2677).
- fixed stuck builds when upstream dependency is skipped, from [@bradrydzewski](https://github.com/bradrydzewski). [#2634](https://github.com/drone/drone/issues/2634).
- fixed issue running steps with dependencies on failure, from [@bradrydzewski](https://github.com/bradrydzewski). [#2667](https://github.com/drone/drone/issues/2667).
## [1.0.1] - 2019-04-10
### Added
- pass stage environment variables to pipeline steps, by [@bradrydzewski](https://github.com/bradrydzewski).
- update go-scm to version 1.3.0, by [@bradrydzewski](https://github.com/bradrydzewski).
- update drone-runtime to version to 1.0.4, by [@bradrydzewski](https://github.com/bradrydzewski).
- ping docker daemon before agent starts to ensure connectivity, by [@bradrydzewski](https://github.com/bradrydzewski).

238
LICENSE
View File

@ -1,201 +1,89 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright 2019 Drone.IO, Inc.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
The Drone Community Edition is licensed under the Apache License,
Version 2.0 (the "Apache License"). You may obtain a copy of the
Apache License at
1. Definitions.
http://www.apache.org/licenses/LICENSE-2.0
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
The Drone Enterprise Edition is licensed under the Drone
Non-Commercial License (the "Non-Commercial License"). A copy of
the Non-Commercial License is provided below.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
The source files in this repository have a header indicating
which license they are under. The BUILDING_OSS file provides
instructions for creating the Community Edition distribution
subject to the terms of the Apache License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
-----------------------------------------------------------------
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
Drone Non-Commercial License
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
Contributor: Drone.IO, Inc.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
Source Code: https://github.com/harness/drone
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
This license lets you use and share this software for free,
with a trial-length time limit on commercial use. Specifically:
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
If you follow the rules below, you may do everything with this
software that would otherwise infringe either the contributor's
copyright in it, any patent claim the contributor can license
that covers this software as of the contributor's latest
contribution, or both.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
1. You must limit use of this software in any manner primarily
intended for or directed toward commercial advantage or
private monetary compensation to a trial period of 32
consecutive calendar days. This limit does not apply to use in
developing feedback, modifications, or extensions that you
contribute back to those giving this license.
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Ensure everyone who gets a copy of this software from you, in
source code or any other form, gets the text of this license
and the contributor and source code lines above.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Do not make any legal claim against anyone for infringing any
patent claim they would infringe by using this software alone,
accusing this software, with or without changes, alone or as
part of a larger application.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
You are excused for unknowingly breaking rule 1 if you stop
doing anything requiring this license within 30 days of
learning you broke the rule.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
**This software comes as is, without any warranty at all. As far
as the law allows, the contributor will not be liable for any
damages related to this software or this license, for any kind of
legal claim.**
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
-----------------------------------------------------------------
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
Waiver: Individual and Small Business
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
Contributor waives the terms of rule 1 for companies meeting all
the following criteria, counting all subsidiaries and affiliated
entities as one:
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
1. worldwide annual gross revenue under $5 million US dollars,
per generally accepted accounting principles
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
2. less than $5 million US dollars in all-time aggregate debt and
equity financing
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
Contributor will not revoke this waiver, but may change terms for
future versions of the software.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
-----------------------------------------------------------------
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
Waiver: Low Usage
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
Contributor waives the terms of rule 1 for companies meeting all
the following criteria, counting all subsidiaries and affiliated
entities as one:
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
1. less than 5,000 total pipelines executed using this software
in the immediately preceding, year-long period
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 Harness, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributor will not revoke this waiver, but may change terms for
future versions of the software.

205
Makefile
View File

@ -1,205 +0,0 @@
ifndef GOPATH
GOPATH := $(shell go env GOPATH)
endif
ifndef GOBIN # derive value from gopath (default to first entry, similar to 'go get')
GOBIN := $(shell go env GOPATH | sed 's/:.*//')/bin
endif
tools = $(addprefix $(GOBIN)/, golangci-lint goimports govulncheck protoc-gen-go protoc-gen-go-grpc gci)
deps = $(addprefix $(GOBIN)/, wire dbmate)
ifneq (,$(wildcard ./.local.env))
include ./.local.env
export
endif
.DEFAULT_GOAL := all
###############################################################################
#
# Initialization
#
###############################################################################
init: ## Install git hooks to perform pre-commit checks
git config core.hooksPath .githooks
git config commit.template .gitmessage
dep: $(deps) ## Install the deps required to generate code and build Harness
@echo "Installing dependencies"
@go mod download
tools: $(tools) ## Install tools required for the build
@echo "Installed tools"
###############################################################################
#
# Harness Build and testing rules
#
###############################################################################
web-build: ## Build the web frontend
@echo "Building web frontend"
@cd web && yarn install && yarn build
build: generate ## Build the all-in-one Harness binary
@echo "Building Harness Server"
go build -o ./gitness ./cmd/gitness
test: generate ## Run the go tests
@echo "Running tests"
@go test -v -coverprofile=coverage.out `go list ./... | egrep -v "./registry/tests/(maven|cargo|gopkg|npm)"`
@go tool cover -html=coverage.out
###############################################################################
#
# Artifact Registry Build and testing rules
#
###############################################################################
run: ar-clean build
./gitness server .local.env || true
# Main conformance test targets
ar-conformance-test: ar-clean build
./gitness server .local.env > logfile.log 2>&1 & echo $$! > server.PID
sleep 20
./registry/tests/conformance_test.sh localhost:3000
@EXIT_CODE=$$?;
@kill `cat server.PID` 2>/dev/null || true
@rm -f server.PID
@rm -f logfile.log
@exit $$EXIT_CODE
ar-hot-conformance-test:
@echo "Running OCI conformance tests..."
rm -rf distribution-spec || true
./registry/tests/conformance_test.sh localhost:3000 || true
@echo "Running Maven conformance tests..."
./registry/tests/maven/scripts/setup_test.sh localhost:3000
@chmod +x /tmp/maven_env.sh
source /tmp/maven_env.sh && go test -v ./registry/tests/maven/... -ginkgo.v || true
@echo "Running Cargo conformance tests..."
./registry/tests/cargo/scripts/setup_test.sh localhost:3000
@chmod +x /tmp/cargo_env.sh
source /tmp/cargo_env.sh && go test -v ./registry/tests/cargo/... -ginkgo.v || true
@chmod +x /tmp/go_env.sh
source /tmp/go_env.sh && go test -v ./registry/tests/gopkg/... -ginkgo.v || true
@echo "Running NPM conformance tests..."
./registry/tests/npm/scripts/setup_test.sh localhost:3000
@chmod +x /tmp/npm_env.sh
source /tmp/npm_env.sh && go test -v ./registry/tests/npm/... -ginkgo.v || true
ar-api-update:
@set -e; \
oapi-codegen --config ./registry/config/openapi/artifact-services.yaml ./registry/app/api/openapi/api.yaml; \
oapi-codegen --config ./registry/config/openapi/artifact-types.yaml ./registry/app/api/openapi/api.yaml;
ar-clean:
@rm artifact-registry 2> /dev/null || true
@docker stop ps_artifacthub 2> /dev/null || true
rm -rf distribution-spec
@kill -9 $$(lsof -t -i:3000) || true
@rm server.PID || true
@rm logfile.log || true
go clean
###############################################################################
#
# Code Formatting and linting
#
###############################################################################
format: tools # Format go code and error if any changes are made
@echo "Formatting ..."
@goimports -w .
@gci write --skip-generated --custom-order -s standard -s "prefix(github.com/harness/gitness)" -s default -s blank -s dot .
@echo "Formatting complete"
modernize:
@echo "Modernizing ..."
@go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...
sec:
@echo "Vulnerability detection $(1)"
@govulncheck ./...
lint: tools generate # lint the golang code - CI
@echo "Linting $(1)"
@golangci-lint run --timeout=5m --verbose --new-from-rev=HEAD~ --whole-files
lint-full: tools generate # full linting the golang code
@echo "Linting $(1)"
@golangci-lint run --timeout=5m --verbose
lint-local: tools generate # lint the golang code - only untracked and staged changes
@echo "Linting $(1)"
@golangci-lint run --new-from-merge-base=main --new --timeout=5m --verbose --whole-files
###############################################################################
# Code Generation
#
# Some code generation can be slow, so we only run it if
# the source file has changed.
###############################################################################
generate: wire
@echo "Generated Code"
wire: cmd/gitness/wire_gen.go
force-wire: ## Force wire code generation
@sh ./scripts/wire/gitness.sh
cmd/gitness/wire_gen.go: cmd/gitness/wire.go
@sh ./scripts/wire/gitness.sh
###############################################################################
# Install Tools and deps
#
# These targets specify the full path to where the tool is installed
# If the tool already exists it wont be re-installed.
###############################################################################
update-tools: delete-tools $(tools) ## Update the tools by deleting and re-installing
delete-tools: ## Delete the tools
@rm $(tools) || true
# Install golangci-lint
$(GOBIN)/golangci-lint:
@echo "🔘 Installing golangci-lint... (`date '+%H:%M:%S'`)"
@curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v2.4.0
# Install goimports to format code
$(GOBIN)/goimports:
@echo "🔘 Installing goimports ... (`date '+%H:%M:%S'`)"
@go install golang.org/x/tools/cmd/goimports
# Install wire to generate dependency injection
$(GOBIN)/wire:
go install github.com/google/wire/cmd/wire@latest
# Install dbmate to perform db migrations
$(GOBIN)/dbmate:
go install github.com/amacneil/dbmate@v1.15.0
$(GOBIN)/govulncheck:
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
$(GOBIN)/protoc-gen-go:
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
$(GOBIN)/protoc-gen-go-grpc:
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
$(GOBIN)/gci:
go install github.com/daixiang0/gci@v0.13.7
help: ## show help message
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[$$()% 0-9a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
.PHONY: delete-tools update-tools help format lint

18
NOTICE
View File

@ -1,12 +1,14 @@
Copyright 2024 Harness, Inc.
Drone
Copyright 2019 Drone.IO, Inc
This product includes software developed at
This product includes software developed at Drone.IO, Inc.
(http://drone.io/).
https://github.com/goharbor/harbor
Licensed under the Apache License, Version 2.0
This product includes software developed by Docker, Inc.
(https://www.docker.com/).
https://github.com/distribution/distribution
Licensed under the Apache License, Version 2.0
This product includes software developed by Canonical Ltd.
(https://www.canonical.com/).
https://gitlab.com/gitlab-org/container-registry
Licensed under the Apache License, Version 2.0
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).

152
README.md
View File

@ -1,152 +0,0 @@
# Harness
Harness Open Source is an open source development platform packed with the power of code hosting, automated DevOps pipelines, hosted development environments (Gitspaces), and artifact registries.
## Overview
Harness Open source is an open source development platform packed with the power of code hosting, automated DevOps pipelines, Gitspaces, and artifact registries.
## Running Harness locally
> The latest publicly released docker image can be found on [harness/harness](https://hub.docker.com/r/harness/harness).
To install Harness yourself, simply run the command below. Once the container is up, you can visit http://localhost:3000 in your browser.
```bash
docker run -d \
-p 3000:3000 \
-p 3022:3022 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/harness:/data \
--name harness \
--restart always \
harness/harness
```
> The Harness image uses a volume to store the database and repositories. It is highly recommended to use a bind mount or named volume as otherwise all data will be lost once the container is stopped.
See [developer.harness.io](https://developer.harness.io/docs/open-source) to learn how to get the most out of Harness.
## Where is Drone?
Harness Open Source represents a massive investment in the next generation of Drone. Where Drone focused solely on continuous integration, Harness adds source code hosting, developer environments (gitspaces), and artifact registries; providing teams with an end-to-end, open source DevOps platform.
The goal is for Harness to eventually be at full parity with Drone in terms of pipeline capabilities, allowing users to seamlessly migrate from Drone to Harness.
But, we expect this to take some time, which is why we took a snapshot of Drone as a feature branch [drone](https://github.com/harness/harness/tree/drone) ([README](https://github.com/harness/harness/blob/drone/.github/readme.md)) so it can continue development.
As for Harness, the development is taking place on the [main](https://github.com/harness/harness/tree/main) branch.
For more information on Harness, please visit [developer.harness.io](https://developer.harness.io/).
For more information on Drone, please visit [drone.io](https://www.drone.io/).
## Harness Open Source Development
### Pre-Requisites
Install the latest stable version of Node and Go version 1.20 or higher, and then install the below Go programs. Ensure the GOPATH [bin directory](https://go.dev/doc/gopath_code#GOPATH) is added to your PATH.
Install protobuf
- Check if you've already installed protobuf ```protoc --version```
- If your version is different than v3.21.11, run ```brew unlink protobuf```
- Get v3.21.11 ```curl -s https://raw.githubusercontent.com/Homebrew/homebrew-core/9de8de7a533609ebfded833480c1f7c05a3448cb/Formula/protobuf.rb > /tmp/protobuf.rb```
- Install it ```brew install /tmp/protobuf.rb```
- Check out your version ```protoc --version```
Install protoc-gen-go and protoc-gen-go-rpc:
- Install protoc-gen-go v1.28.1 ```go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1```
(Note that this will install a binary in $GOBIN so make sure $GOBIN is in your $PATH)
- Install protoc-gen-go-grpc v1.2.0 ```go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0```
```bash
$ make dep
$ make tools
```
### Build
First step is to build the user interface artifacts:
```bash
$ pushd web
$ yarn install
$ yarn build
$ popd
```
After that, you can build the Harness binary:
```bash
$ make build
```
### Run
This project supports all operating systems and architectures supported by Go. This means you can build and run the system on your machine; docker containers are not required for local development and testing.
To start the server at `localhost:3000`, simply run the following command:
```bash
./gitness server .local.env
```
### Auto-Generate Harness API Client used by UI using Swagger
Please make sure to update the autogenerated client code used by the UI when adding new rest APIs.
To regenerate the code, please execute the following steps:
- Regenerate swagger with latest Harness binary `./gitness swagger > web/src/services/code/swagger.yaml`
- navigate to the `web` folder and run `yarn services`
The latest API changes should now be reflected in `web/src/services/code/index.tsx`
# Run Registry Conformance Tests
```
make conformance-test
```
For running conformance tests with existing running service, use:
```
make hot-conformance-test
```
## User Interface
This project includes a full user interface for interacting with the system. When you run the application, you can access the user interface by navigating to `http://localhost:3000` in your browser.
## REST API
This project includes a swagger specification. When you run the application, you can access the swagger specification by navigating to `http://localhost:3000/swagger` in your browser (for raw yaml see `http://localhost:3000/openapi.yaml`).
For registry endpoints, currently swagger is located on different endpoint `http://localhost:3000/registry/swagger/` (for raw json see `http://localhost:3000/registry/swagger.json`). These will be later moved to the main swagger endpoint.
For testing, it's simplest to just use the cli to create a token (this requires Harness server to run):
```bash
# LOGIN (user: admin, pw: changeit)
$ ./gitness login
# GENERATE PAT (1 YEAR VALIDITY)
$ ./gitness user pat "my-pat-uid" 2592000
```
The command outputs a valid PAT that has been granted full access as the user.
The token can then be send as part of the `Authorization` header with Postman or curl:
```bash
$ curl http://localhost:3000/api/v1/user \
-H "Authorization: Bearer $TOKEN"
```
## CLI
This project includes VERY basic command line tools for development and running the service. Please remember that you must start the server before you can execute commands.
For a full list of supported operations, please see
```bash
$ ./gitness --help
```
## Contributing
Refer to [CONTRIBUTING.md](https://github.com/harness/harness/blob/main/CONTRIBUTING.md)
## License
Apache License 2.0, see [LICENSE](https://github.com/harness/harness/blob/main/LICENSE).

129
Taskfile.yml Normal file
View File

@ -0,0 +1,129 @@
# https://taskfile.org
version: '2'
tasks:
install:
dir: cmd/drone-server
cmds: [ go install -v ]
env:
GO111MODULE: on
build:
cmds:
- task: build-base
vars: { name: server }
build-base:
env:
GOOS: linux
GOARCH: amd64
CGO_ENABLED: '0'
GO111MODULE: 'on'
cmds:
- cmd: >
go build -o release/linux/amd64/drone-{{.name}}
github.com/drone/drone/cmd/drone-{{.name}}
cleanup:
cmds:
- rm -rf release
docker:
cmds:
- task: docker-base
vars: { name: server, image: drone/drone }
docker-base:
vars:
GIT_BRANCH:
sh: git rev-parse --abbrev-ref HEAD
cmds:
- cmd: docker rmi {{.image}}
ignore_error: true
- cmd: docker rmi {{.image}}:{{.GIT_BRANCH}}
ignore_error: true
- cmd: >
docker build --rm
-f docker/Dockerfile.{{.name}}.linux.amd64
-t {{.image}} .
- cmd: >
docker tag {{.image}} {{.image}}:{{.GIT_BRANCH}}
test:
cmds:
- go test ./...
env:
GO111MODULE: 'on'
test-mysql:
env:
DRONE_DATABASE_DRIVER: mysql
DRONE_DATABASE_DATASOURCE: root@tcp(localhost:3306)/test?parseTime=true
GO111MODULE: 'on'
cmds:
- cmd: docker kill mysql
silent: true
ignore_error: true
- cmd: >
docker run
-p 3306:3306
--env MYSQL_DATABASE=test
--env MYSQL_ALLOW_EMPTY_PASSWORD=yes
--name mysql
--detach
--rm
mysql:5.7
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
- cmd: go test -count=1 github.com/drone/drone/store/batch
- cmd: go test -count=1 github.com/drone/drone/store/batch2
- cmd: go test -count=1 github.com/drone/drone/store/build
- cmd: go test -count=1 github.com/drone/drone/store/card
- cmd: go test -count=1 github.com/drone/drone/store/cron
- cmd: go test -count=1 github.com/drone/drone/store/logs
- cmd: go test -count=1 github.com/drone/drone/store/perm
- cmd: go test -count=1 github.com/drone/drone/store/repos
- cmd: go test -count=1 github.com/drone/drone/store/secret
- cmd: go test -count=1 github.com/drone/drone/store/secret/global
- cmd: go test -count=1 github.com/drone/drone/store/stage
- cmd: go test -count=1 github.com/drone/drone/store/step
- cmd: go test -count=1 github.com/drone/drone/store/template
- cmd: go test -count=1 github.com/drone/drone/store/user
- cmd: docker kill mysql
test-postgres:
env:
DRONE_DATABASE_DRIVER: postgres
DRONE_DATABASE_DATASOURCE: host=localhost user=postgres password=postgres dbname=postgres sslmode=disable
GO111MODULE: 'on'
cmds:
- cmd: docker kill postgres
ignore_error: true
silent: false
- silent: false
cmd: >
docker run
-p 5432:5432
--env POSTGRES_PASSWORD=postgres
--env POSTGRES_USER=postgres
--name postgres
--detach
--rm
postgres:9-alpine
- cmd: go test -count=1 github.com/drone/drone/store/batch
- cmd: go test -count=1 github.com/drone/drone/store/batch2
- cmd: go test -count=1 github.com/drone/drone/store/build
- cmd: go test -count=1 github.com/drone/drone/store/card
- cmd: go test -count=1 github.com/drone/drone/store/cron
- cmd: go test -count=1 github.com/drone/drone/store/logs
- cmd: go test -count=1 github.com/drone/drone/store/perm
- cmd: go test -count=1 github.com/drone/drone/store/repos
- cmd: go test -count=1 github.com/drone/drone/store/secret
- cmd: go test -count=1 github.com/drone/drone/store/secret/global
- cmd: go test -count=1 github.com/drone/drone/store/stage
- cmd: go test -count=1 github.com/drone/drone/store/step
- cmd: go test -count=1 github.com/drone/drone/store/template
- cmd: go test -count=1 github.com/drone/drone/store/user
- cmd: docker kill postgres
silent: true

View File

@ -1,15 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package api

View File

@ -1,149 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
var (
ErrUnauthorized = errors.New("unauthorized")
ErrForbidden = errors.New("forbidden")
ErrParentResourceTypeUnknown = errors.New("Unknown parent resource type")
ErrPrincipalTypeUnknown = errors.New("Unknown principal type")
)
// Check checks if a resource specific permission is granted for the current auth session in the scope.
// Returns nil if the permission is granted, otherwise returns an error.
func Check(
ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
scope *types.Scope, resource *types.Resource, permission enum.Permission,
) error {
authenticated, err := authorizer.Check(
ctx,
session,
scope,
resource,
permission,
)
if err != nil {
return err
}
return CheckSessionAuth(session, authenticated)
}
// CheckAll checks if multiple resources specific permission is granted for the current auth session in the scope.
// Returns nil if the permission is granted, otherwise returns an error.
func CheckAll(
ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
permissionChecks ...types.PermissionCheck,
) error {
hasPermission, err := authorizer.CheckAll(
ctx,
session,
permissionChecks...,
)
if err != nil {
return err
}
return CheckSessionAuth(session, hasPermission)
}
// CheckSessionAuth returns nil if the user is authenticated.
// Otherwise, ir returns err unauthorized on anonymous or err forbidden on non anonymous session.
func CheckSessionAuth(session *auth.Session, authenticated bool) error {
if !authenticated {
if auth.IsAnonymousSession(session) {
return ErrUnauthorized
}
return ErrForbidden
}
return nil
}
// IsNoAccess returns true if the error is ErrUnauthorized or ErrForbidden.
func IsNoAccess(err error) bool {
return errors.Is(err, ErrForbidden) || errors.Is(err, ErrUnauthorized)
}
// CheckChild checks if a resource specific permission is granted for the current auth session
// in the scope of a parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckChild(
ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
spaceStore store.SpaceStore, repoStore store.RepoStore, parentType enum.ParentResourceType, parentID int64,
resourceType enum.ResourceType, resourceName string, permission enum.Permission,
) error {
scope, err := getScopeForParent(ctx, spaceStore, repoStore, parentType, parentID)
if err != nil {
return err
}
resource := &types.Resource{
Type: resourceType,
Identifier: resourceName,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}
// getScopeForParent Returns the scope for a given resource parent (space or repo).
func getScopeForParent(
ctx context.Context, spaceStore store.SpaceStore, repoStore store.RepoStore,
parentType enum.ParentResourceType, parentID int64,
) (*types.Scope, error) {
// TODO: Can this be done cleaner?
switch parentType {
case enum.ParentResourceTypeSpace:
space, err := spaceStore.Find(ctx, parentID)
if err != nil {
return nil, fmt.Errorf("parent space not found: %w", err)
}
return &types.Scope{SpacePath: space.Path}, nil
case enum.ParentResourceTypeRepo:
repo, err := repoStore.Find(ctx, parentID)
if err != nil {
return nil, fmt.Errorf("parent repo not found: %w", err)
}
spacePath, repoName, err := paths.DisectLeaf(repo.Path)
if err != nil {
return nil, fmt.Errorf("failed to disect path '%s': %w", repo.Path, err)
}
return &types.Scope{SpacePath: spacePath, Repo: repoName}, nil
default:
log.Ctx(ctx).Debug().Msgf("Unsupported parent type encountered: '%s'", parentType)
return nil, ErrParentResourceTypeUnknown
}
}

View File

@ -1,45 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckConnector checks if a repo specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckConnector(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
parentPath,
identifier string,
permission enum.Permission,
) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypeConnector,
Identifier: identifier,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,45 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckGitspace checks if a gitspace specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckGitspace(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
parentPath,
identifier string,
permission enum.Permission,
) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypeGitspace,
Identifier: identifier,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,45 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckInfraProvider checks if a gitspace specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckInfraProvider(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
parentPath,
identifier string,
permission enum.Permission,
) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypeInfraProvider,
Identifier: identifier,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,44 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckPipeline checks if a pipeline specific permission is granted for the current auth session
// in the scope of the parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckPipeline(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
repoPath string, pipelineIdentifier string, permission enum.Permission) error {
spacePath, repoName, err := paths.DisectLeaf(repoPath)
if err != nil {
return fmt.Errorf("failed to disect path '%s': %w", repoPath, err)
}
scope := &types.Scope{SpacePath: spacePath, Repo: repoName}
resource := &types.Resource{
Type: enum.ResourceTypePipeline,
Identifier: pipelineIdentifier,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,36 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
)
// CheckRegistry checks if a registry specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckRegistry(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
permissionChecks ...types.PermissionCheck,
) error {
return CheckAll(ctx, authorizer, session, permissionChecks...)
}

View File

@ -1,120 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"fmt"
"slices"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckRepo checks if a repo specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckRepo(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
repo *types.RepositoryCore,
permission enum.Permission,
) error {
parentSpace, name, err := paths.DisectLeaf(repo.Path)
if err != nil {
return fmt.Errorf("failed to disect path '%s': %w", repo.Path, err)
}
scope := &types.Scope{SpacePath: parentSpace}
resource := &types.Resource{
Type: enum.ResourceTypeRepo,
Identifier: name,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}
func IsRepoOwner(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
repo *types.RepositoryCore,
) (bool, error) {
// for now we use repoedit as permission to verify if someone is a SpaceOwner and hence a RepoOwner.
err := CheckRepo(ctx, authorizer, session, repo, enum.PermissionRepoEdit)
if err != nil && !IsNoAccess(err) {
return false, fmt.Errorf("failed to check access user access: %w", err)
}
return err == nil, nil
}
// CheckRepoState checks if requested permission is allowed given the state of the repository.
func CheckRepoState(
_ context.Context,
_ *auth.Session,
repo *types.RepositoryCore,
reqPermission enum.Permission,
additionalAllowedRepoStates ...enum.RepoState,
) error {
permissionsAllowedPerRepoState := map[enum.RepoState][]enum.Permission{
enum.RepoStateActive: {
enum.PermissionRepoView,
enum.PermissionRepoCreate,
enum.PermissionRepoEdit,
enum.PermissionRepoPush,
enum.PermissionRepoReview,
enum.PermissionRepoDelete,
enum.PermissionRepoReportCommitCheck,
enum.PermissionPipelineView,
enum.PermissionPipelineExecute,
enum.PermissionPipelineEdit,
enum.PermissionPipelineDelete,
enum.PermissionServiceAccountView,
},
enum.RepoStateArchived: {
enum.PermissionRepoView,
enum.PermissionPipelineView,
enum.PermissionServiceAccountView,
},
// allowed permissions for repos on transition states during import/migration are handled by their controller.
enum.RepoStateGitImport: {},
enum.RepoStateMigrateDataImport: {},
enum.RepoStateMigrateGitPush: {},
}
if len(additionalAllowedRepoStates) > 0 && slices.Contains(additionalAllowedRepoStates, repo.State) {
return nil
}
defaultAllowedPermissions := permissionsAllowedPerRepoState[repo.State]
if !slices.Contains(defaultAllowedPermissions, reqPermission) {
return errors.PreconditionFailedf("Operation is not allowed for repository in state %s", repo.State)
}
return nil
}

View File

@ -1,39 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckSecret checks if a repo specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckSecret(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
parentPath, identifier string, permission enum.Permission) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypeSecret,
Identifier: identifier,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,40 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckService checks if a service specific permission is granted for the current auth session.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckService(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
svc *types.Service, permission enum.Permission,
) error {
// a service exists outside any scope
scope := &types.Scope{}
resource := &types.Resource{
Type: enum.ResourceTypeService,
Identifier: svc.UID,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,37 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/types/enum"
)
// CheckServiceAccount checks if a service account specific permission is granted for the current auth session
// in the scope of the parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckServiceAccount(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
spaceStore store.SpaceStore, repoStore store.RepoStore, parentType enum.ParentResourceType, parentID int64,
saUID string, permission enum.Permission,
) error {
return CheckChild(ctx, authorizer, session,
spaceStore, repoStore, parentType, parentID,
enum.ResourceTypeServiceAccount, saUID, permission)
}

View File

@ -1,70 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckSpace checks if a space specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if permission is granted, otherwise returns NotAuthenticated, NotAuthorized, or the underlying error.
func CheckSpace(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
space *types.SpaceCore,
permission enum.Permission,
) error {
parentSpace, name, err := paths.DisectLeaf(space.Path)
if err != nil {
return fmt.Errorf("failed to disect path '%s': %w", space.Path, err)
}
scope := &types.Scope{SpacePath: parentSpace}
resource := &types.Resource{
Type: enum.ResourceTypeSpace,
Identifier: name,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}
// CheckSpaceScope checks if a specific permission is granted for the current auth session
// in the scope of the provided space.
// Returns nil if permission is granted, otherwise returns NotAuthenticated, NotAuthorized, or the underlying error.
func CheckSpaceScope(
ctx context.Context,
authorizer authz.Authorizer,
session *auth.Session,
space *types.SpaceCore,
resourceType enum.ResourceType,
permission enum.Permission,
) error {
scope := &types.Scope{SpacePath: space.Path}
resource := &types.Resource{
Type: resourceType,
Identifier: "",
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,39 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckTemplate checks if a repo specific permission is granted for the current auth session
// in the scope of its parent.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckTemplate(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
parentPath, identifier string, permission enum.Permission) error {
scope := &types.Scope{SpacePath: parentPath}
resource := &types.Resource{
Type: enum.ResourceTypeTemplate,
Identifier: identifier,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,40 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package auth
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// CheckUser checks if a user specific permission is granted for the current auth session.
// Returns nil if the permission is granted, otherwise returns an error.
// NotAuthenticated, NotAuthorized, or any underlying error.
func CheckUser(ctx context.Context, authorizer authz.Authorizer, session *auth.Session,
user *types.User, permission enum.Permission,
) error {
// a user exists outside any scope
scope := &types.Scope{}
resource := &types.Resource{
Type: enum.ResourceTypeUser,
Identifier: user.UID,
}
return Check(ctx, authorizer, session, scope, resource, permission)
}

View File

@ -1,65 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListChecks return an array of status check results for a commit in a repository.
func (c *Controller) ListChecks(
ctx context.Context,
session *auth.Session,
repoRef string,
commitSHA string,
opts types.CheckListOptions,
) ([]types.Check, int, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
if err != nil {
return nil, 0, fmt.Errorf("failed to acquire access to repo: %w", err)
}
var checks []types.Check
var count int
err = c.tx.WithTx(ctx, func(ctx context.Context) (err error) {
checks, err = c.checkStore.List(ctx, repo.ID, commitSHA, opts)
if err != nil {
return fmt.Errorf("failed to list status check results for repo=%s: %w", repo.Identifier, err)
}
if opts.Page == 1 && len(checks) < opts.Size {
count = len(checks)
return nil
}
count, err = c.checkStore.Count(ctx, repo.ID, commitSHA, opts)
if err != nil {
return fmt.Errorf("failed to count status check results for repo=%s: %w", repo.Identifier, err)
}
return nil
})
if err != nil {
return nil, 0, err
}
return checks, count, nil
}

View File

@ -1,49 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"context"
"fmt"
"time"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListRecentChecks return an array of status check UIDs that have been run recently.
func (c *Controller) ListRecentChecks(
ctx context.Context,
session *auth.Session,
repoRef string,
opts types.CheckRecentOptions,
) ([]string, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
}
if opts.Since == 0 {
opts.Since = time.Now().Add(-30 * 24 * time.Hour).UnixMilli()
}
checkIdentifiers, err := c.checkStore.ListRecent(ctx, repo.ID, opts)
if err != nil {
return nil, fmt.Errorf("failed to list status check results for repo=%s: %w", repo.Identifier, err)
}
return checkIdentifiers, nil
}

View File

@ -1,63 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"context"
"fmt"
"time"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListRecentChecksSpace return an array of status check UIDs that have been run recently.
func (c *Controller) ListRecentChecksSpace(
ctx context.Context,
session *auth.Session,
spaceRef string,
recursive bool,
opts types.CheckRecentOptions,
) ([]string, error) {
space, err := c.getSpaceCheckAccess(ctx, session, spaceRef, enum.PermissionSpaceView)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to space: %w", err)
}
if opts.Since == 0 {
opts.Since = time.Now().Add(-30 * 24 * time.Hour).UnixMilli()
}
var spaceIDs []int64
if recursive {
spaceIDs, err = c.spaceStore.GetDescendantsIDs(ctx, space.ID)
if err != nil {
return nil, fmt.Errorf("failed to get space descendants ids: %w", err)
}
} else {
spaceIDs = append(spaceIDs, space.ID)
}
checkIdentifiers, err := c.checkStore.ListRecentSpace(ctx, spaceIDs, opts)
if err != nil {
return nil, fmt.Errorf(
"failed to list status check results for space=%s: %w",
space.Identifier, err,
)
}
return checkIdentifiers, nil
}

View File

@ -1,246 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"regexp"
"time"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
events "github.com/harness/gitness/app/events/check"
"github.com/harness/gitness/git"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type ReportInput struct {
// TODO [CODE-1363]: remove after identifier migration.
CheckUID string `json:"check_uid" deprecated:"true"`
Identifier string `json:"identifier"`
Status enum.CheckStatus `json:"status"`
Summary string `json:"summary"`
Link string `json:"link"`
Payload types.CheckPayload `json:"payload"`
Started int64 `json:"started,omitempty"`
Ended int64 `json:"ended,omitempty"`
}
// TODO: Can we drop the '$' - depends on whether harness allows it.
var regexpCheckIdentifier = "^[0-9a-zA-Z-_.$]{1,127}$"
var matcherCheckIdentifier = regexp.MustCompile(regexpCheckIdentifier)
// Sanitize validates and sanitizes the ReportInput data.
func (in *ReportInput) Sanitize(
sanitizers map[enum.CheckPayloadKind]func(in *ReportInput, s *auth.Session) error, session *auth.Session,
) error {
// TODO [CODE-1363]: remove after identifier migration.
if in.Identifier == "" {
in.Identifier = in.CheckUID
}
if in.Identifier == "" {
return usererror.BadRequest("Identifier is missing")
}
if !matcherCheckIdentifier.MatchString(in.Identifier) {
return usererror.BadRequestf("Identifier must match the regular expression: %s", regexpCheckIdentifier)
}
_, ok := in.Status.Sanitize()
if !ok {
return usererror.BadRequest("Invalid value provided for status check status")
}
validatorFn, ok := sanitizers[in.Payload.Kind]
if !ok {
return usererror.BadRequest("Invalid value provided for the payload kind")
}
// Validate and sanitize the input data based on version; Require a link... and similar operations.
if err := validatorFn(in, session); err != nil {
return fmt.Errorf("payload validation failed: %w", err)
}
if in.Ended != 0 && in.Ended < in.Started {
return usererror.BadRequest("Started time reported after ended time")
}
return nil
}
func SanitizeJSONPayload(source json.RawMessage, data any) (json.RawMessage, error) {
if len(source) == 0 {
return json.Marshal(data) // marshal the empty object
}
decoder := json.NewDecoder(bytes.NewReader(source))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&data); err != nil {
return nil, usererror.BadRequestf("Payload data doesn't match the required format: %s", err.Error())
}
buffer := bytes.NewBuffer(nil)
buffer.Grow(512)
encoder := json.NewEncoder(buffer)
encoder.SetEscapeHTML(false)
if err := encoder.Encode(data); err != nil {
return nil, fmt.Errorf("failed to sanitize json payload: %w", err)
}
result := buffer.Bytes()
if result[len(result)-1] == '\n' {
result = result[:len(result)-1]
}
return result, nil
}
// Report modifies an existing or creates a new (if none yet exists) status check report for a specific commit.
func (c *Controller) Report(
ctx context.Context,
session *auth.Session,
repoRef string,
commitSHA string,
in *ReportInput,
metadata map[string]string,
) (*types.Check, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoReportCommitCheck)
if err != nil {
return nil, fmt.Errorf("failed to acquire access to repo: %w", err)
}
if errValidate := in.Sanitize(c.sanitizers, session); errValidate != nil {
return nil, errValidate
}
if !git.ValidateCommitSHA(commitSHA) {
return nil, usererror.BadRequest("Invalid commit SHA provided")
}
_, err = c.git.GetCommit(ctx, &git.GetCommitParams{
ReadParams: git.ReadParams{RepoUID: repo.GitUID},
Revision: commitSHA,
})
if err != nil {
return nil, fmt.Errorf("failed to commit sha=%s: %w", commitSHA, err)
}
now := time.Now().UnixMilli()
metadataJSON, _ := json.Marshal(metadata)
existingCheck, err := c.checkStore.FindByIdentifier(ctx, repo.ID, commitSHA, in.Identifier)
if err != nil && !errors.Is(err, store.ErrResourceNotFound) {
return nil, fmt.Errorf("failed to find existing check for Identifier %q: %w", in.Identifier, err)
}
started := getStartTime(in, existingCheck, now)
ended := getEndTime(in, now)
statusCheckReport := &types.Check{
CreatedBy: session.Principal.ID,
Created: now,
Updated: now,
RepoID: repo.ID,
CommitSHA: commitSHA,
Identifier: in.Identifier,
Status: in.Status,
Summary: in.Summary,
Link: in.Link,
Payload: in.Payload,
Metadata: metadataJSON,
ReportedBy: session.Principal.ToPrincipalInfo(),
Started: started,
Ended: ended,
}
err = c.checkStore.Upsert(ctx, statusCheckReport)
if err != nil {
return nil, fmt.Errorf("failed to upsert status check result for repo=%s: %w", repo.Identifier, err)
}
c.eventReporter.Reported(ctx, &events.ReportedPayload{
Base: events.Base{
RepoID: repo.ID,
SHA: commitSHA,
},
Identifier: in.Identifier,
Status: in.Status,
})
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeStatusCheckReportUpdated, statusCheckReport)
return statusCheckReport, nil
}
func getStartTime(in *ReportInput, check types.Check, now int64) int64 {
// start value came in api
if in.Started != 0 {
return in.Started
}
// in.started has no value we smartly put value for started
// in case of pending we assume check has not started running
if in.Status == enum.CheckStatusPending {
return 0
}
// new check
if check.Started == 0 {
return now
}
// The incoming check status can now be running or terminal.
// in case we already have running status we don't update time else we return current time as check has started
// running.
if check.Status == enum.CheckStatusRunning {
return check.Started
}
// Note: In case of reporting terminal statuses again and again we have assumed its
// a report of new status check everytime.
// In case someone reports any status before marking running return current time.
// This can happen if someone only reports terminal status or marks running status again after terminal.
return now
}
func getEndTime(in *ReportInput, now int64) int64 {
// end value came in api
if in.Ended != 0 {
return in.Ended
}
// if we get terminal status i.e. error, failure or success we return current time.
if in.Status.IsCompleted() {
return now
}
// in case of other status we return value as 0, which means we have not yet completed the check.
return 0
}

View File

@ -1,215 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"testing"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func Test_getStartedTime(t *testing.T) {
type args struct {
in *ReportInput
check types.Check
now int64
}
tests := []struct {
name string
args args
want int64
}{
{
name: "nothing to pending",
args: args{
in: &ReportInput{Status: enum.CheckStatusPending},
check: types.Check{},
now: 1234,
},
want: 0,
},
{
name: "nothing to running",
args: args{
in: &ReportInput{Status: enum.CheckStatusRunning},
check: types.Check{},
now: 1234,
},
want: 1234,
},
{
name: "nothing to completed",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess},
check: types.Check{},
now: 1234,
},
want: 1234,
},
{
name: "nothing to completed1",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess, Started: 1},
check: types.Check{},
now: 1234,
},
want: 1,
},
{
name: "pending to pending",
args: args{
in: &ReportInput{Status: enum.CheckStatusPending, Started: 1},
check: types.Check{Status: enum.CheckStatusPending, Started: 0},
now: 1234,
},
want: 1,
},
{
name: "pending to pending1",
args: args{
in: &ReportInput{Status: enum.CheckStatusPending},
check: types.Check{Status: enum.CheckStatusPending, Started: 0},
now: 1234,
},
want: 0,
},
{
name: "pending to running",
args: args{
in: &ReportInput{Status: enum.CheckStatusRunning},
check: types.Check{Status: enum.CheckStatusPending, Started: 0},
now: 1234,
},
want: 1234,
},
{
name: "pending to running1",
args: args{
in: &ReportInput{Status: enum.CheckStatusRunning, Started: 1},
check: types.Check{Status: enum.CheckStatusPending, Started: 0},
now: 1234,
},
want: 1,
},
{
name: "pending to completed",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess, Started: 1},
check: types.Check{Status: enum.CheckStatusPending, Started: 0},
now: 1234,
},
want: 1,
},
{
name: "pending to completed1",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess},
check: types.Check{Status: enum.CheckStatusPending, Started: 0},
now: 1234,
},
want: 1234,
},
{
name: "running to pending",
args: args{
in: &ReportInput{Status: enum.CheckStatusPending, Started: 1},
check: types.Check{Status: enum.CheckStatusRunning, Started: 9876},
now: 1234,
},
want: 1,
},
{
name: "running to pending1",
args: args{
in: &ReportInput{Status: enum.CheckStatusPending},
check: types.Check{Status: enum.CheckStatusRunning, Started: 9876},
now: 1234,
},
want: 0,
},
{
name: "running to running",
args: args{
in: &ReportInput{Status: enum.CheckStatusRunning},
check: types.Check{Status: enum.CheckStatusRunning, Started: 9876},
now: 1234,
},
want: 9876,
},
{
name: "running to running1",
args: args{
in: &ReportInput{Status: enum.CheckStatusRunning, Started: 1},
check: types.Check{Status: enum.CheckStatusRunning, Started: 9876},
now: 1234,
},
want: 1,
},
{
name: "running to completed",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess},
check: types.Check{Status: enum.CheckStatusRunning, Started: 9876},
now: 1234,
},
want: 9876,
},
{
name: "running to completed",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess, Started: 1},
check: types.Check{Status: enum.CheckStatusRunning, Started: 9876},
now: 1234,
},
want: 1,
},
{
name: "completed to pending",
args: args{
in: &ReportInput{Status: enum.CheckStatusPending},
check: types.Check{Status: enum.CheckStatusSuccess, Started: 9876},
now: 1234,
},
want: 0,
},
{
name: "completed to running",
args: args{
in: &ReportInput{Status: enum.CheckStatusRunning},
check: types.Check{Status: enum.CheckStatusSuccess, Started: 9876},
now: 1234,
},
want: 1234,
},
{
name: "completed to completed",
args: args{
in: &ReportInput{Status: enum.CheckStatusSuccess},
check: types.Check{Status: enum.CheckStatusSuccess, Started: 9876},
now: 1234,
},
want: 1234,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getStartTime(tt.args.in, tt.args.check, tt.args.now); got != tt.want {
t.Errorf("getStartTime() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -1,110 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/space"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
checkevents "github.com/harness/gitness/app/events/check"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/sse"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/git"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type Controller struct {
tx dbtx.Transactor
authorizer authz.Authorizer
spaceStore store.SpaceStore
checkStore store.CheckStore
spaceFinder refcache.SpaceFinder
repoFinder refcache.RepoFinder
git git.Interface
sanitizers map[enum.CheckPayloadKind]func(in *ReportInput, s *auth.Session) error
sseStreamer sse.Streamer
eventReporter *checkevents.Reporter
}
func NewController(
tx dbtx.Transactor,
authorizer authz.Authorizer,
spaceStore store.SpaceStore,
checkStore store.CheckStore,
spaceFinder refcache.SpaceFinder,
repoFinder refcache.RepoFinder,
git git.Interface,
sanitizers map[enum.CheckPayloadKind]func(in *ReportInput, s *auth.Session) error,
sseStreamer sse.Streamer,
eventReporter *checkevents.Reporter,
) *Controller {
return &Controller{
tx: tx,
authorizer: authorizer,
spaceStore: spaceStore,
checkStore: checkStore,
spaceFinder: spaceFinder,
repoFinder: repoFinder,
git: git,
sanitizers: sanitizers,
sseStreamer: sseStreamer,
eventReporter: eventReporter,
}
}
//nolint:unparam
func (c *Controller) getRepoCheckAccess(
ctx context.Context,
session *auth.Session,
repoRef string,
reqPermission enum.Permission,
allowedRepoStates ...enum.RepoState,
) (*types.RepositoryCore, error) {
if repoRef == "" {
return nil, usererror.BadRequest("A valid repository reference must be provided.")
}
repo, err := c.repoFinder.FindByRef(ctx, repoRef)
if err != nil {
return nil, fmt.Errorf("failed to find repository: %w", err)
}
if err := apiauth.CheckRepoState(ctx, session, repo, reqPermission, allowedRepoStates...); err != nil {
return nil, err
}
if err = apiauth.CheckRepo(ctx, c.authorizer, session, repo, reqPermission); err != nil {
return nil, fmt.Errorf("access check failed: %w", err)
}
return repo, nil
}
func (c *Controller) getSpaceCheckAccess(
ctx context.Context,
session *auth.Session,
spaceRef string,
permission enum.Permission,
) (*types.SpaceCore, error) {
return space.GetSpaceCheckAuth(ctx, c.spaceFinder, c.authorizer, session, spaceRef, permission)
}

View File

@ -1,75 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func ProvideCheckSanitizers() map[enum.CheckPayloadKind]func(in *ReportInput, s *auth.Session) error {
registeredCheckSanitizers := make(map[enum.CheckPayloadKind]func(in *ReportInput, s *auth.Session) error)
registeredCheckSanitizers[enum.CheckPayloadKindEmpty] = createEmptyPayloadSanitizer()
registeredCheckSanitizers[enum.CheckPayloadKindRaw] = createRawPayloadSanitizer()
// Markdown and Raw are the same.
registeredCheckSanitizers[enum.CheckPayloadKindMarkdown] = registeredCheckSanitizers[enum.CheckPayloadKindRaw]
registeredCheckSanitizers[enum.CheckPayloadKindPipeline] = createPipelinePayloadSanitizer()
return registeredCheckSanitizers
}
func createEmptyPayloadSanitizer() func(in *ReportInput, _ *auth.Session) error {
return func(in *ReportInput, _ *auth.Session) error {
// the default payload kind (empty) does not support the payload data: clear it here
in.Payload.Version = ""
in.Payload.Data = []byte("{}")
if in.Link == "" { // the link is mandatory as there is nothing in the payload
return usererror.BadRequest("Link is missing")
}
return nil
}
}
func createRawPayloadSanitizer() func(in *ReportInput, _ *auth.Session) error {
return func(in *ReportInput, _ *auth.Session) error {
// the text payload kinds (raw and markdown) do not support the version
if in.Payload.Version != "" {
return usererror.BadRequestf("Payload version must be empty for the payload kind '%s'",
in.Payload.Kind)
}
payloadDataJSON, err := SanitizeJSONPayload(in.Payload.Data, &types.CheckPayloadText{})
if err != nil {
return err
}
in.Payload.Data = payloadDataJSON
return nil
}
}
func createPipelinePayloadSanitizer() func(in *ReportInput, _ *auth.Session) error {
return func(_ *ReportInput, _ *auth.Session) error {
return usererror.BadRequest("Kind cannot be pipeline for external checks")
}
}

View File

@ -1,61 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
checkevents "github.com/harness/gitness/app/events/check"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/sse"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/git"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types/enum"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideCheckSanitizers,
ProvideController,
)
func ProvideController(
tx dbtx.Transactor,
authorizer authz.Authorizer,
spaceStore store.SpaceStore,
checkStore store.CheckStore,
spaceFinder refcache.SpaceFinder,
repoFinder refcache.RepoFinder,
git git.Interface,
sanitizers map[enum.CheckPayloadKind]func(in *ReportInput, s *auth.Session) error,
sseStreamer sse.Streamer,
eventReporter *checkevents.Reporter,
) *Controller {
return NewController(
tx,
authorizer,
spaceStore,
checkStore,
spaceFinder,
repoFinder,
git,
sanitizers,
sseStreamer,
eventReporter,
)
}

View File

@ -1,43 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/connector"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store"
)
type Controller struct {
connectorStore store.ConnectorStore
connectorService *connector.Service
spaceFinder refcache.SpaceFinder
authorizer authz.Authorizer
}
func NewController(
authorizer authz.Authorizer,
connectorStore store.ConnectorStore,
connectorService *connector.Service,
spaceFinder refcache.SpaceFinder,
) *Controller {
return &Controller{
connectorStore: connectorStore,
connectorService: connectorService,
spaceFinder: spaceFinder,
authorizer: authorizer,
}
}

View File

@ -1,116 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"context"
"fmt"
"strconv"
"strings"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
var (
// errConnectorRequiresParent if the user tries to create a connector without a parent space.
errConnectorRequiresParent = usererror.BadRequest(
"Parent space required - standalone connector are not supported.")
)
type CreateInput struct {
Description string `json:"description"`
SpaceRef string `json:"space_ref"` // Ref of the parent space
Identifier string `json:"identifier"`
Type enum.ConnectorType `json:"type"`
types.ConnectorConfig
}
func (c *Controller) Create(
ctx context.Context,
session *auth.Session,
in *CreateInput,
) (*types.Connector, error) {
if err := in.validate(); err != nil {
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}
parentSpace, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref: %w", err)
}
err = apiauth.CheckConnector(
ctx,
c.authorizer,
session,
parentSpace.Path,
"",
enum.PermissionConnectorEdit,
)
if err != nil {
return nil, err
}
now := time.Now().UnixMilli()
connector := &types.Connector{
Description: in.Description,
CreatedBy: session.Principal.ID,
Type: in.Type,
SpaceID: parentSpace.ID,
Identifier: in.Identifier,
Created: now,
Updated: now,
Version: 0,
ConnectorConfig: in.ConnectorConfig,
}
err = c.connectorStore.Create(ctx, connector)
if err != nil {
return nil, fmt.Errorf("connector creation failed: %w", err)
}
return connector, nil
}
func (in *CreateInput) validate() error {
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
return errConnectorRequiresParent
}
// check that the connector type is valid
if _, ok := in.Type.Sanitize(); !ok {
return usererror.BadRequest("Invalid connector type")
}
// if the connector type is valid, validate the connector config
if err := in.ConnectorConfig.Validate(in.Type); err != nil {
return usererror.BadRequest(fmt.Sprintf("invalid connector config: %s", err.Error()))
}
if err := check.Identifier(in.Identifier); err != nil {
return err
}
in.Description = strings.TrimSpace(in.Description)
return check.Description(in.Description)
}

View File

@ -1,46 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Delete(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) error {
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckConnector(ctx, c.authorizer, session, space.Path, identifier, enum.PermissionConnectorDelete)
if err != nil {
return fmt.Errorf("failed to authorize: %w", err)
}
err = c.connectorStore.DeleteByIdentifier(ctx, space.ID, identifier)
if err != nil {
return fmt.Errorf("could not delete connector: %w", err)
}
return nil
}

View File

@ -1,49 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Find(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) (*types.Connector, error) {
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckConnector(ctx, c.authorizer, session, space.Path, identifier, enum.PermissionConnectorView)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
connector, err := c.connectorStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find connector: %w", err)
}
return connector, nil
}

View File

@ -1,67 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
func (c *Controller) Test(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) (types.ConnectorTestResponse, error) {
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return types.ConnectorTestResponse{}, fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckConnector(ctx, c.authorizer, session, space.Path, identifier, enum.PermissionConnectorAccess)
if err != nil {
return types.ConnectorTestResponse{}, fmt.Errorf("failed to authorize: %w", err)
}
connector, err := c.connectorStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return types.ConnectorTestResponse{}, fmt.Errorf("failed to find connector: %w", err)
}
resp, err := c.connectorService.Test(ctx, connector)
if err != nil {
return types.ConnectorTestResponse{}, err
}
// Try to update connector last test information in DB. Log but ignore errors
_, err = c.connectorStore.UpdateOptLock(ctx, connector, func(original *types.Connector) error {
original.LastTestErrorMsg = resp.ErrorMsg
original.LastTestStatus = resp.Status
original.LastTestAttempt = time.Now().UnixMilli()
return nil
})
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("failed to update test connection information in connector")
}
return resp, nil
}

View File

@ -1,100 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"context"
"fmt"
"strings"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
// UpdateInput is used for updating a connector.
type UpdateInput struct {
Identifier *string `json:"identifier"`
Description *string `json:"description"`
*types.ConnectorConfig
}
func (c *Controller) Update(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
in *UpdateInput,
) (*types.Connector, error) {
if err := in.validate(); err != nil {
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckConnector(ctx, c.authorizer, session, space.Path, identifier, enum.PermissionConnectorEdit)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
connector, err := c.connectorStore.FindByIdentifier(ctx, space.ID, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find connector: %w", err)
}
return c.connectorStore.UpdateOptLock(ctx, connector, func(original *types.Connector) error {
if in.Identifier != nil {
original.Identifier = *in.Identifier
}
if in.Description != nil {
original.Description = *in.Description
}
// TODO: See if this can be made better. The PATCH API supports partial updates so
// currently we keep all the top level fields the same unless they are explicitly provided.
// The connector config is a nested field so we only check whether it's provided at the top level, and not
// all the fields inside the config. Maybe PUT/POST would be a better option here?
// We can revisit this once we start adding more connectors.
if in.ConnectorConfig != nil {
if err := in.ConnectorConfig.Validate(connector.Type); err != nil {
return usererror.BadRequestf("Failed to validate connector config: %s", err.Error())
}
original.ConnectorConfig = *in.ConnectorConfig
}
return nil
})
}
func (in *UpdateInput) validate() error {
if in.Identifier != nil {
if err := check.Identifier(*in.Identifier); err != nil {
return err
}
}
if in.Description != nil {
*in.Description = strings.TrimSpace(*in.Description)
if err := check.Description(*in.Description); err != nil {
return err
}
}
return nil
}

View File

@ -1,38 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package connector
import (
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/connector"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideController,
)
func ProvideController(
connectorStore store.ConnectorStore,
connectorService *connector.Service,
authorizer authz.Authorizer,
spaceFinder refcache.SpaceFinder,
) *Controller {
return NewController(authorizer, connectorStore, connectorService, spaceFinder)
}

View File

@ -1,69 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/pipeline/checks"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
func (c *Controller) Cancel(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
executionNum int64,
) (*types.Execution, error) {
repo, err := c.getRepoCheckPipelineAccess(
ctx,
session,
repoRef,
pipelineIdentifier,
enum.PermissionPipelineExecute,
)
if err != nil {
return nil, err
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return nil, fmt.Errorf("failed to find pipeline: %w", err)
}
execution, err := c.executionStore.FindByNumber(ctx, pipeline.ID, executionNum)
if err != nil {
return nil, fmt.Errorf("failed to find execution %d: %w", executionNum, err)
}
err = c.canceler.Cancel(ctx, repo, execution)
if err != nil {
return nil, fmt.Errorf("unable to cancel execution: %w", err)
}
// Write to the checks store, log and ignore on errors
err = checks.Write(ctx, c.checkStore, execution, pipeline)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("could not update status check")
}
return execution, nil
}

View File

@ -1,106 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/pipeline/canceler"
"github.com/harness/gitness/app/pipeline/commit"
"github.com/harness/gitness/app/pipeline/triggerer"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
type Controller struct {
tx dbtx.Transactor
authorizer authz.Authorizer
executionStore store.ExecutionStore
checkStore store.CheckStore
canceler canceler.Canceler
commitService commit.Service
triggerer triggerer.Triggerer
stageStore store.StageStore
pipelineStore store.PipelineStore
repoFinder refcache.RepoFinder
}
func NewController(
tx dbtx.Transactor,
authorizer authz.Authorizer,
executionStore store.ExecutionStore,
checkStore store.CheckStore,
canceler canceler.Canceler,
commitService commit.Service,
triggerer triggerer.Triggerer,
stageStore store.StageStore,
pipelineStore store.PipelineStore,
repoFinder refcache.RepoFinder,
) *Controller {
return &Controller{
tx: tx,
authorizer: authorizer,
executionStore: executionStore,
checkStore: checkStore,
canceler: canceler,
commitService: commitService,
triggerer: triggerer,
stageStore: stageStore,
pipelineStore: pipelineStore,
repoFinder: repoFinder,
}
}
// getRepoCheckPipelineAccess fetches a repo, checks if the permission is allowed based on the repo state,
// and checks if the current user has permission to access pipelines belong to it.
//
//nolint:unparam
func (c *Controller) getRepoCheckPipelineAccess(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
reqPermission enum.Permission,
allowedRepoStates ...enum.RepoState,
) (*types.RepositoryCore, error) {
repo, err := c.repoFinder.FindByRef(ctx, repoRef)
if err != nil {
return nil, fmt.Errorf("failed to find repo by ref: %w", err)
}
if err := apiauth.CheckRepoState(ctx, session, repo, reqPermission, allowedRepoStates...); err != nil {
return nil, err
}
err = apiauth.CheckPipeline(
ctx,
c.authorizer,
session,
repo.Path,
pipelineIdentifier,
reqPermission)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
return repo, nil
}

View File

@ -1,84 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/pipeline/triggerer"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/drone/go-scm/scm"
)
func (c *Controller) Create(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
branch string,
) (*types.Execution, error) {
repo, err := c.getRepoCheckPipelineAccess(ctx, session, repoRef, pipelineIdentifier, enum.PermissionPipelineExecute)
if err != nil {
return nil, err
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return nil, fmt.Errorf("failed to find pipeline: %w", err)
}
// If the branch is empty, use the default branch specified in the pipeline.
// It that is also empty, use the repo default branch.
if branch == "" {
branch = pipeline.DefaultBranch
if branch == "" {
branch = repo.DefaultBranch
}
}
// expand the branch to a git reference.
ref := scm.ExpandRef(branch, "refs/heads")
// Fetch the commit information from the commits service.
commit, err := c.commitService.FindRef(ctx, repo, ref)
if err != nil {
return nil, fmt.Errorf("failed to fetch commit: %w", err)
}
// Create manual hook for execution.
hook := &triggerer.Hook{
Trigger: session.Principal.UID, // who/what triggered the build, different from commit author
AuthorLogin: commit.Author.Identity.Name,
TriggeredBy: session.Principal.ID,
AuthorName: commit.Author.Identity.Name,
AuthorEmail: commit.Author.Identity.Email,
Ref: ref,
Message: commit.Message,
Title: commit.Title,
Before: commit.SHA.String(),
After: commit.SHA.String(),
Sender: session.Principal.UID,
Source: branch,
Target: branch,
Params: map[string]string{},
Timestamp: commit.Author.When.UnixMilli(),
}
// Trigger the execution
return c.triggerer.Trigger(ctx, pipeline, hook)
}

View File

@ -1,54 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Delete(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
executionNum int64,
) error {
repo, err := c.getRepoCheckPipelineAccess(
ctx,
session,
repoRef,
pipelineIdentifier,
enum.PermissionPipelineDelete,
)
if err != nil {
return err
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return fmt.Errorf("failed to find pipeline: %w", err)
}
err = c.executionStore.Delete(ctx, pipeline.ID, executionNum)
if err != nil {
return fmt.Errorf("could not delete execution: %w", err)
}
return nil
}

View File

@ -1,64 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Find(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
executionNum int64,
) (*types.Execution, error) {
repo, err := c.getRepoCheckPipelineAccess(
ctx,
session,
repoRef,
pipelineIdentifier,
enum.PermissionPipelineView,
)
if err != nil {
return nil, err
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return nil, fmt.Errorf("failed to find pipeline: %w", err)
}
execution, err := c.executionStore.FindByNumber(ctx, pipeline.ID, executionNum)
if err != nil {
return nil, fmt.Errorf("failed to find execution %d: %w", executionNum, err)
}
stages, err := c.stageStore.ListWithSteps(ctx, execution.ID)
if err != nil {
return nil, fmt.Errorf("could not query stage information for execution %d: %w",
executionNum, err)
}
// Add stages information to the execution
execution.Stages = stages
return execution, nil
}

View File

@ -1,71 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) List(
ctx context.Context,
session *auth.Session,
repoRef string,
pipelineIdentifier string,
pagination types.Pagination,
) ([]*types.Execution, int64, error) {
repo, err := c.getRepoCheckPipelineAccess(
ctx,
session,
repoRef,
pipelineIdentifier,
enum.PermissionPipelineView,
)
if err != nil {
return nil, 0, err
}
pipeline, err := c.pipelineStore.FindByIdentifier(ctx, repo.ID, pipelineIdentifier)
if err != nil {
return nil, 0, fmt.Errorf("failed to find pipeline: %w", err)
}
var count int64
var executions []*types.Execution
err = c.tx.WithTx(ctx, func(ctx context.Context) (err error) {
count, err = c.executionStore.Count(ctx, pipeline.ID)
if err != nil {
return fmt.Errorf("failed to count child executions: %w", err)
}
executions, err = c.executionStore.List(ctx, pipeline.ID, pagination)
if err != nil {
return fmt.Errorf("failed to list child executions: %w", err)
}
return
}, dbtx.TxDefaultReadOnly)
if err != nil {
return executions, count, fmt.Errorf("failed to fetch list: %w", err)
}
return executions, count, nil
}

View File

@ -1,48 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package execution
import (
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/pipeline/canceler"
"github.com/harness/gitness/app/pipeline/commit"
"github.com/harness/gitness/app/pipeline/triggerer"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideController,
)
func ProvideController(
tx dbtx.Transactor,
authorizer authz.Authorizer,
executionStore store.ExecutionStore,
checkStore store.CheckStore,
canceler canceler.Canceler,
commitService commit.Service,
triggerer triggerer.Triggerer,
stageStore store.StageStore,
pipelineStore store.PipelineStore,
repoFinder refcache.RepoFinder,
) *Controller {
return NewController(tx, authorizer, executionStore, checkStore,
canceler, commitService, triggerer, stageStore, pipelineStore, repoFinder)
}

View File

@ -1,141 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"errors"
"fmt"
"github.com/harness/gitness/app/githook"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
"github.com/rs/zerolog/log"
)
var _ hook.ClientFactory = (*ControllerClientFactory)(nil)
var _ hook.Client = (*ControllerClient)(nil)
// ControllerClientFactory creates clients that directly call the controller to execute githooks.
type ControllerClientFactory struct {
githookCtrl *Controller
git git.Interface
}
func (f *ControllerClientFactory) NewClient(envVars map[string]string) (hook.Client, error) {
payload, err := hook.LoadPayloadFromMap[githook.Payload](envVars)
if err != nil {
return nil, fmt.Errorf("failed to load payload from provided map of environment variables: %w", err)
}
// ensure we return disabled message in case it's explicitly disabled
if payload.Disabled {
return hook.NewNoopClient([]string{"hook disabled"}), nil
}
if err := payload.Validate(); err != nil {
return nil, fmt.Errorf("payload validation failed: %w", err)
}
return &ControllerClient{
baseInput: githook.GetInputBaseFromPayload(payload),
githookCtrl: f.githookCtrl,
git: f.git,
}, nil
}
// ControllerClient directly calls the controller to execute githooks.
type ControllerClient struct {
baseInput types.GithookInputBase
githookCtrl *Controller
git RestrictedGIT
}
func (c *ControllerClient) PreReceive(
ctx context.Context,
in hook.PreReceiveInput,
) (hook.Output, error) {
log.Ctx(ctx).Debug().Int64("repo_id", c.baseInput.RepoID).Msg("calling pre-receive")
out, err := c.githookCtrl.PreReceive(
ctx,
c.git, // Harness doesn't require any custom git connector.
nil, // TODO: update once githooks are auth protected
types.GithookPreReceiveInput{
GithookInputBase: c.baseInput,
PreReceiveInput: in,
},
)
if err != nil {
return hook.Output{}, translateControllerError(err)
}
return out, nil
}
func (c *ControllerClient) Update(
ctx context.Context,
in hook.UpdateInput,
) (hook.Output, error) {
log.Ctx(ctx).Debug().Int64("repo_id", c.baseInput.RepoID).Msg("calling update")
out, err := c.githookCtrl.Update(
ctx,
c.git, // Harness doesn't require any custom git connector.
nil, // TODO: update once githooks are auth protected
types.GithookUpdateInput{
GithookInputBase: c.baseInput,
UpdateInput: in,
},
)
if err != nil {
return hook.Output{}, translateControllerError(err)
}
return out, nil
}
func (c *ControllerClient) PostReceive(
ctx context.Context,
in hook.PostReceiveInput,
) (hook.Output, error) {
log.Ctx(ctx).Debug().Int64("repo_id", c.baseInput.RepoID).Msg("calling post-receive")
out, err := c.githookCtrl.PostReceive(
ctx,
c.git, // Harness doesn't require any custom git connector.
nil, // TODO: update once githooks are auth protected
types.GithookPostReceiveInput{
GithookInputBase: c.baseInput,
PostReceiveInput: in,
},
)
if err != nil {
return hook.Output{}, translateControllerError(err)
}
return out, nil
}
func translateControllerError(err error) error {
if errors.Is(err, store.ErrResourceNotFound) {
return hook.ErrNotFound
}
return err
}

View File

@ -1,237 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"fmt"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/auth/authz"
gitevents "github.com/harness/gitness/app/events/git"
repoevents "github.com/harness/gitness/app/events/repo"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/services/usergroup"
"github.com/harness/gitness/app/sse"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/audit"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/api"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/git/sha"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/rs/zerolog/log"
)
type Controller struct {
authorizer authz.Authorizer
principalStore store.PrincipalStore
repoStore store.RepoStore
repoFinder refcache.RepoFinder
gitReporter *gitevents.Reporter
repoReporter *repoevents.Reporter
pullreqStore store.PullReqStore
urlProvider url.Provider
protectionManager *protection.Manager
limiter limiter.ResourceLimiter
settings *settings.Service
preReceiveExtender PreReceiveExtender
updateExtender UpdateExtender
postReceiveExtender PostReceiveExtender
sseStreamer sse.Streamer
lfsStore store.LFSObjectStore
auditService audit.Service
userGroupService usergroup.Service
}
func NewController(
authorizer authz.Authorizer,
principalStore store.PrincipalStore,
repoStore store.RepoStore,
repoFinder refcache.RepoFinder,
gitReporter *gitevents.Reporter,
repoReporter *repoevents.Reporter,
pullreqStore store.PullReqStore,
urlProvider url.Provider,
protectionManager *protection.Manager,
limiter limiter.ResourceLimiter,
settings *settings.Service,
preReceiveExtender PreReceiveExtender,
updateExtender UpdateExtender,
postReceiveExtender PostReceiveExtender,
sseStreamer sse.Streamer,
lfsStore store.LFSObjectStore,
auditService audit.Service,
userGroupService usergroup.Service,
) *Controller {
return &Controller{
authorizer: authorizer,
principalStore: principalStore,
repoStore: repoStore,
repoFinder: repoFinder,
gitReporter: gitReporter,
repoReporter: repoReporter,
pullreqStore: pullreqStore,
urlProvider: urlProvider,
protectionManager: protectionManager,
limiter: limiter,
settings: settings,
preReceiveExtender: preReceiveExtender,
updateExtender: updateExtender,
postReceiveExtender: postReceiveExtender,
sseStreamer: sseStreamer,
lfsStore: lfsStore,
auditService: auditService,
userGroupService: userGroupService,
}
}
func (c *Controller) getRepoCheckAccess(
ctx context.Context,
_ *auth.Session,
repoID int64,
_ enum.Permission,
) (*types.RepositoryCore, error) {
if repoID < 1 {
return nil, usererror.BadRequest("A valid repository reference must be provided.")
}
repo, err := c.repoFinder.FindByID(ctx, repoID)
if err != nil {
return nil, fmt.Errorf("failed to find repo with id %d: %w", repoID, err)
}
// repo state check is done in pre-receive.
// TODO: execute permission check. block anything but Harness service?
return repo, nil
}
// GetBaseSHAForScanningChanges returns the commit sha to which the new sha of the reference
// should be compared against when scanning incoming changes.
// NOTE: If no such a sha exists, then (sha.None, false, nil) is returned.
// This will happen in case the default branch doesn't exist yet.
func GetBaseSHAForScanningChanges(
ctx context.Context,
rgit RestrictedGIT,
repo *types.RepositoryCore,
env hook.Environment,
refUpdates []hook.ReferenceUpdate,
findBaseFor hook.ReferenceUpdate,
) (sha.SHA, bool, error) {
// always return old SHA of ref if possible (even if ref was deleted, that's on the caller)
if !findBaseFor.Old.IsNil() {
return findBaseFor.Old, true, nil
}
// reference is just being created.
// For now we use default branch as a fallback (can be optimized to most recent commit on reference that exists)
dfltBranchFullRef := api.BranchPrefix + repo.DefaultBranch
for _, refUpdate := range refUpdates {
if refUpdate.Ref != dfltBranchFullRef {
continue
}
// default branch is being updated as part of push - make sure we use OLD default branch sha for comparison
if !refUpdate.Old.IsNil() {
return refUpdate.Old, true, nil
}
// default branch is being created - no fallback available
return sha.None, false, nil
}
// read default branch from git
dfltBranchOut, err := rgit.GetBranch(ctx, &git.GetBranchParams{
ReadParams: git.ReadParams{
RepoUID: repo.GitUID,
AlternateObjectDirs: env.AlternateObjectDirs,
},
BranchName: repo.DefaultBranch,
})
if errors.IsNotFound(err) {
// this happens for empty repo's where the default branch wasn't created yet.
return sha.None, false, nil
}
if err != nil {
return sha.None, false, fmt.Errorf("failed to get default branch from git: %w", err)
}
return dfltBranchOut.Branch.SHA, true, nil
}
func isForcePush(
ctx context.Context,
rgit RestrictedGIT,
gitUID string,
alternateObjectDirs []string,
refUpdate hook.ReferenceUpdate,
) (bool, error) {
if refUpdate.Old.IsNil() || refUpdate.New.IsNil() {
return false, nil
}
if isTag(refUpdate.Ref) {
return true, nil
}
result, err := rgit.IsAncestor(ctx, git.IsAncestorParams{
ReadParams: git.ReadParams{
RepoUID: gitUID,
AlternateObjectDirs: alternateObjectDirs,
},
AncestorCommitSHA: refUpdate.Old,
DescendantCommitSHA: refUpdate.New,
})
if err != nil {
return false, err
}
return !result.Ancestor, nil
}
func logOutputFor(ctx context.Context, hookName string, output hook.Output) {
event := log.Ctx(ctx).Info()
if output.Error != nil {
event = event.Str("output.error", *output.Error)
}
if len(output.Messages) > 0 {
filteredMsgs := make([]string, 0, len(output.Messages)/2+1)
for _, msg := range output.Messages {
if msg == "" {
continue
}
filteredMsgs = append(filteredMsgs, msg)
}
const maxMessageLines = 16
if len(filteredMsgs) > maxMessageLines {
filteredMsgs = append(filteredMsgs[:maxMessageLines], fmt.Sprintf("... %d more", len(filteredMsgs)-maxMessageLines))
}
event = event.Strs("output.messages", filteredMsgs)
}
event.Msgf("%s hook output", hookName)
}

View File

@ -1,110 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/types"
)
type PreReceiveExtender interface {
Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.RepositoryCore,
types.GithookPreReceiveInput,
*hook.Output,
) error
}
type UpdateExtender interface {
Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.RepositoryCore,
types.GithookUpdateInput,
*hook.Output,
) error
}
type PostReceiveExtender interface {
Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.RepositoryCore,
types.GithookPostReceiveInput,
*hook.Output,
) error
}
type NoOpPreReceiveExtender struct {
}
func NewPreReceiveExtender() PreReceiveExtender {
return NoOpPreReceiveExtender{}
}
func (NoOpPreReceiveExtender) Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.RepositoryCore,
types.GithookPreReceiveInput,
*hook.Output,
) error {
return nil
}
type NoOpUpdateExtender struct {
}
func NewUpdateExtender() UpdateExtender {
return NoOpUpdateExtender{}
}
func (NoOpUpdateExtender) Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.RepositoryCore,
types.GithookUpdateInput,
*hook.Output,
) error {
return nil
}
type NoOpPostReceiveExtender struct {
}
func NewPostReceiveExtender() PostReceiveExtender {
return NoOpPostReceiveExtender{}
}
func (NoOpPostReceiveExtender) Extend(
context.Context,
RestrictedGIT,
*auth.Session,
*types.RepositoryCore,
types.GithookPostReceiveInput,
*hook.Output,
) error {
return nil
}

View File

@ -1,40 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/api"
)
// RestrictedGIT is a git client that is restricted to a subset of operations of git.Interface
// which can be executed on quarantine data that is part of git-hooks (e.g. pre-receive, update, ..)
// and don't alter the repo (so only read operations).
// NOTE: While it doesn't apply to all git-hooks (e.g. post-receive), we still use the interface across the board
// to "soft enforce" no write operations being executed as part of githooks.
type RestrictedGIT interface {
IsAncestor(ctx context.Context, params git.IsAncestorParams) (git.IsAncestorOutput, error)
ScanSecrets(ctx context.Context, param *git.ScanSecretsParams) (*git.ScanSecretsOutput, error)
GetBranch(ctx context.Context, params *git.GetBranchParams) (*git.GetBranchOutput, error)
Diff(ctx context.Context, in *git.DiffParams, files ...api.FileDiffRequest) (<-chan *git.FileDiff, <-chan error)
GetBlob(ctx context.Context, params *git.GetBlobParams) (*git.GetBlobOutput, error)
ProcessPreReceiveObjects(
ctx context.Context,
params git.ProcessPreReceiveObjectsParams,
) (git.ProcessPreReceiveObjectsOutput, error)
MergeBase(ctx context.Context, params git.MergeBaseParams) (git.MergeBaseOutput, error)
}

View File

@ -1,605 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"fmt"
"slices"
"strings"
"time"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/bootstrap"
gitevents "github.com/harness/gitness/app/events/git"
repoevents "github.com/harness/gitness/app/events/repo"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/audit"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/git"
gitapi "github.com/harness/gitness/git/api"
gitenum "github.com/harness/gitness/git/enum"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/git/sha"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/gotidy/ptr"
"github.com/rs/zerolog/log"
)
const (
// gitReferenceNamePrefixBranch is the prefix of references of type branch.
gitReferenceNamePrefixBranch = "refs/heads/"
// gitReferenceNamePrefixTag is the prefix of references of type tag.
gitReferenceNamePrefixTag = "refs/tags/"
// gitReferenceNamePrefixTag is the prefix of pull req references.
gitReferenceNamePullReq = "refs/pullreq/"
)
// refForcePushMap stores branch refs that were force pushed.
type refForcePushMap map[string]struct{}
// PostReceive executes the post-receive hook for a git repository.
func (c *Controller) PostReceive(
ctx context.Context,
rgit RestrictedGIT,
session *auth.Session,
in types.GithookPostReceiveInput,
) (hook.Output, error) {
repoCore, err := c.getRepoCheckAccess(ctx, session, in.RepoID, enum.PermissionRepoPush)
if err != nil {
return hook.Output{}, err
}
repo, err := c.repoStore.Find(ctx, repoCore.ID)
if err != nil {
return hook.Output{}, err
}
// create output object and have following messages fill its messages
out := hook.Output{}
defer func() {
logOutputFor(ctx, "post-receive", out)
}()
// update default branch based on ref update info on empty repos.
// as the branch could be different than the configured default value.
c.handleEmptyRepoPush(ctx, repo, in.PostReceiveInput, &out)
// always update last git push time - best effort
c.updateLastGITPushTime(ctx, repo)
// report ref events if repo is in an active state - best effort
forcePushStatus := make(refForcePushMap)
if repo.State == enum.RepoStateActive {
forcePushStatus = c.reportReferenceEvents(ctx, rgit, repo, in.PrincipalID, in.PostReceiveInput)
}
// handle branch updates related to PRs - best effort
c.handlePRMessaging(ctx, rgit, repo, in.PostReceiveInput, &out)
err = c.postReceiveExtender.Extend(ctx, rgit, session, repo.Core(), in, &out)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to extend post-receive hook: %w", err)
}
c.logForcePush(ctx, repo, in.PrincipalID, in.RefUpdates, forcePushStatus)
c.repoReporter.Pushed(ctx, &repoevents.PushedPayload{
Base: repoevents.Base{
RepoID: in.RepoID,
PrincipalID: in.PrincipalID,
},
})
return out, nil
}
// reportReferenceEvents is reporting reference events to the event system.
// NOTE: keep best effort for now as it doesn't change the outcome of the git operation.
// TODO: in the future we might want to think about propagating errors so user is aware of events not being triggered.
func (c *Controller) reportReferenceEvents(
ctx context.Context,
rgit RestrictedGIT,
repo *types.Repository,
principalID int64,
in hook.PostReceiveInput,
) refForcePushMap {
forcePushStatus := make(refForcePushMap)
for _, refUpdate := range in.RefUpdates {
switch {
case strings.HasPrefix(refUpdate.Ref, gitReferenceNamePrefixBranch):
if forced := c.reportBranchEvent(ctx, rgit, repo, principalID, in.Environment, refUpdate); forced {
forcePushStatus[refUpdate.Ref] = struct{}{}
}
case strings.HasPrefix(refUpdate.Ref, gitReferenceNamePrefixTag):
c.reportTagEvent(ctx, repo, principalID, refUpdate)
default:
// Ignore any other references in post-receive
}
}
return forcePushStatus
}
func (c *Controller) reportBranchEvent(
ctx context.Context,
rgit RestrictedGIT,
repo *types.Repository,
principalID int64,
env hook.Environment,
branchUpdate hook.ReferenceUpdate,
) bool {
var forced bool
switch {
case branchUpdate.Old.IsNil():
payload := &gitevents.BranchCreatedPayload{
RepoID: repo.ID,
PrincipalID: principalID,
Ref: branchUpdate.Ref,
SHA: branchUpdate.New.String(),
}
c.gitReporter.BranchCreated(ctx, payload)
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeBranchCreated, payload)
case branchUpdate.New.IsNil():
payload := &gitevents.BranchDeletedPayload{
RepoID: repo.ID,
PrincipalID: principalID,
Ref: branchUpdate.Ref,
SHA: branchUpdate.Old.String(),
}
c.gitReporter.BranchDeleted(ctx, payload)
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeBranchDeleted, payload)
default:
// A force update event might trigger some additional operations that aren't required
// for ordinary updates (force pushes alter the commit history of a branch).
var err error
forced, err = isForcePush(ctx, rgit, repo.GitUID, env.AlternateObjectDirs, branchUpdate)
if err != nil {
// In case of an error consider this a forced update. In post-update the branch has already been updated,
// so there's less harm in declaring the update as forced.
forced = true
log.Ctx(ctx).Warn().Err(err).
Str("ref", branchUpdate.Ref).
Msg("failed to check ancestor")
}
payload := &gitevents.BranchUpdatedPayload{
RepoID: repo.ID,
PrincipalID: principalID,
Ref: branchUpdate.Ref,
OldSHA: branchUpdate.Old.String(),
NewSHA: branchUpdate.New.String(),
Forced: forced,
}
c.gitReporter.BranchUpdated(ctx, payload)
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeBranchUpdated, payload)
}
return forced
}
func (c *Controller) reportTagEvent(
ctx context.Context,
repo *types.Repository,
principalID int64,
tagUpdate hook.ReferenceUpdate,
) {
switch {
case tagUpdate.Old.IsNil():
payload := &gitevents.TagCreatedPayload{
RepoID: repo.ID,
PrincipalID: principalID,
Ref: tagUpdate.Ref,
SHA: tagUpdate.New.String(),
}
c.gitReporter.TagCreated(ctx, payload)
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeTagCreated, payload)
case tagUpdate.New.IsNil():
payload := &gitevents.TagDeletedPayload{
RepoID: repo.ID,
PrincipalID: principalID,
Ref: tagUpdate.Ref,
SHA: tagUpdate.Old.String(),
}
c.gitReporter.TagDeleted(ctx, payload)
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeTagDeleted, payload)
default:
payload := &gitevents.TagUpdatedPayload{
RepoID: repo.ID,
PrincipalID: principalID,
Ref: tagUpdate.Ref,
OldSHA: tagUpdate.Old.String(),
NewSHA: tagUpdate.New.String(),
// tags can only be force updated!
Forced: true,
}
c.gitReporter.TagUpdated(ctx, payload)
c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypeTagUpdated, payload)
}
}
// handlePRMessaging checks any single branch push for pr information and returns an according response if needed.
// TODO: If it is a new branch, or an update on a branch without any PR, it also sends out an SSE for pr creation.
func (c *Controller) handlePRMessaging(
ctx context.Context,
rgit RestrictedGIT,
sourceRepo *types.Repository,
in hook.PostReceiveInput,
out *hook.Output,
) {
// skip anything that was a batch push / isn't branch related / isn't updating/creating a branch.
if len(in.RefUpdates) != 1 ||
!strings.HasPrefix(in.RefUpdates[0].Ref, gitReferenceNamePrefixBranch) ||
in.RefUpdates[0].New.IsNil() {
return
}
// for now we only care about first branch that was pushed.
refUpdate := in.RefUpdates[0]
branchName := refUpdate.Ref[len(gitReferenceNamePrefixBranch):]
newSHA := refUpdate.New
c.suggestPullRequest(ctx, rgit, sourceRepo, branchName, newSHA, out)
// TODO: store latest pushed branch for user in cache and send out SSE
}
func (c *Controller) suggestPullRequest(
ctx context.Context,
rgit RestrictedGIT,
sourceRepo *types.Repository,
branchName string,
newSHA sha.SHA,
out *hook.Output,
) {
// Find the most recent few open PRs created from this branch.
prs, err := c.pullreqStore.List(ctx, &types.PullReqFilter{
Page: 1,
Size: 10,
SourceRepoID: sourceRepo.ID,
SourceBranch: branchName,
// we only care about open PRs - merged/closed will lead to "create new PR" message
States: []enum.PullReqState{enum.PullReqStateOpen},
Order: enum.OrderDesc,
Sort: enum.PullReqSortCreated,
// don't care about the PR description, omit it from the response
ExcludeDescription: true,
})
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msgf(
"failed to find pullrequests for branch '%s' originating from repo '%s'",
branchName,
sourceRepo.Path,
)
return
}
slices.Reverse(prs) // Use ascending order for message output.
// For already existing PRs, check if the merge base is still unique and if there are PR with non-unique merge base
// print them to users terminal to inform about pending closure.
var prsNonUniqueMergeBase []*types.PullReq
for _, pr := range prs {
if pr.SourceRepoID == nil || *pr.SourceRepoID != pr.TargetRepoID {
continue
}
var targetBranch string
targetBranch, err = git.GetRefPath(pr.TargetBranch, gitenum.RefTypeBranch)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msgf(
"failed to create target reference from target branch'%s' originating from repo '%s'",
pr.TargetBranch,
sourceRepo.Path,
)
continue
}
_, err = rgit.MergeBase(ctx, git.MergeBaseParams{
ReadParams: git.ReadParams{RepoUID: sourceRepo.GitUID},
Ref1: targetBranch,
Ref2: newSHA.String(),
})
if errors.IsInvalidArgument(err) || gitapi.IsUnrelatedHistoriesError(err) {
prsNonUniqueMergeBase = append(prsNonUniqueMergeBase, pr)
continue
}
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msgf(
"failed to find merge base for PR #%d originating from repo '%s'",
pr.Number,
sourceRepo.Path,
)
continue
}
}
msgs, err := c.getNonUniqueMergeBasePRsMessages(ctx, sourceRepo, branchName, prsNonUniqueMergeBase)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("failed to get messages for open pull request")
return
}
if len(msgs) > 0 {
out.Messages = append(out.Messages, msgs...)
return
}
// For already existing PRs, print them to users terminal for easier access.
msgs, err = c.getOpenPRsMessages(ctx, sourceRepo, branchName, prs)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("failed to get messages for open pull request")
return
}
if len(msgs) > 0 {
out.Messages = append(out.Messages, msgs...)
return
}
if branchName == sourceRepo.DefaultBranch {
// Don't suggest a pull request if this is a push to the default branch.
return
}
// This is a new PR!
out.Messages = append(out.Messages,
fmt.Sprintf("Create a pull request for %q by visiting:", branchName),
" "+c.urlProvider.GenerateUICompareURL(ctx, sourceRepo.Path, sourceRepo.DefaultBranch, branchName),
)
}
func (c *Controller) getOpenPRsMessages(
ctx context.Context,
sourceRepo *types.Repository,
branchName string,
prs []*types.PullReq,
) ([]string, error) {
if len(prs) == 0 {
return nil, nil
}
msgs := make([]string, 0, 2*len(prs)+1)
if len(prs) == 1 {
msgs = append(msgs, fmt.Sprintf("Branch %q has an open PR:", branchName))
} else {
msgs = append(msgs, fmt.Sprintf("Branch %q has open PRs:", branchName))
}
msgs, err := c.appendPRs(ctx, prs, sourceRepo, msgs)
if err != nil {
return nil, fmt.Errorf("failed to append PRs: %w", err)
}
return msgs, nil
}
func (c *Controller) getNonUniqueMergeBasePRsMessages(
ctx context.Context,
sourceRepo *types.Repository,
branchName string,
prs []*types.PullReq,
) ([]string, error) {
if len(prs) == 0 {
return nil, nil
}
msgs := make([]string, 0, 2*len(prs)+1)
if len(prs) == 1 {
msgs = append(msgs,
fmt.Sprintf("Branch %q has an open PR that would be closed because non-unique merge base:", branchName))
} else {
msgs = append(msgs,
fmt.Sprintf("Branch %q has open PRs that would be closed because non-unique merge base:", branchName))
}
msgs, err := c.appendPRs(ctx, prs, sourceRepo, msgs)
if err != nil {
return nil, fmt.Errorf("failed to append PRs: %w", err)
}
return msgs, nil
}
func (c *Controller) appendPRs(
ctx context.Context,
prs []*types.PullReq,
sourceRepo *types.Repository,
msgs []string,
) ([]string, error) {
for _, pr := range prs {
path := sourceRepo.Path
if pr.TargetRepoID != *pr.SourceRepoID {
targetRepo, err := c.repoFinder.FindByID(ctx, pr.TargetRepoID)
if err != nil {
return nil, fmt.Errorf("failed to find target repo by ID: %w", err)
}
path = targetRepo.Path
}
msgs = append(msgs, fmt.Sprintf(" (#%d) %s", pr.Number, pr.Title))
msgs = append(msgs, " "+c.urlProvider.GenerateUIPRURL(ctx, path, pr.Number))
}
return msgs, nil
}
// handleEmptyRepoPush updates repo default branch on empty repos if push contains branches.
func (c *Controller) handleEmptyRepoPush(
ctx context.Context,
repo *types.Repository,
in hook.PostReceiveInput,
out *hook.Output,
) {
if !repo.IsEmpty {
return
}
var newDefaultBranch string
// update default branch if corresponding branch does not exist
for _, refUpdate := range in.RefUpdates {
if strings.HasPrefix(refUpdate.Ref, gitReferenceNamePrefixBranch) && !refUpdate.New.IsNil() {
branchName := refUpdate.Ref[len(gitReferenceNamePrefixBranch):]
if branchName == repo.DefaultBranch {
newDefaultBranch = branchName
break
}
// use the first pushed branch if default branch is not present.
if newDefaultBranch == "" {
newDefaultBranch = branchName
}
}
}
if newDefaultBranch == "" {
out.Error = ptr.String(usererror.ErrEmptyRepoNeedsBranch.Error())
return
}
oldName := repo.DefaultBranch
var err error
repo, err = c.repoStore.UpdateOptLock(ctx, repo, func(r *types.Repository) error {
r.IsEmpty = false
r.DefaultBranch = newDefaultBranch
return nil
})
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msgf("failed to update the repo default branch to %s and is_empty to false",
newDefaultBranch)
return
}
c.repoFinder.MarkChanged(ctx, repo.Core())
if repo.DefaultBranch != oldName {
c.repoReporter.DefaultBranchUpdated(ctx, &repoevents.DefaultBranchUpdatedPayload{
Base: repoevents.Base{
RepoID: repo.ID,
PrincipalID: bootstrap.NewSystemServiceSession().Principal.ID,
},
OldName: oldName,
NewName: repo.DefaultBranch,
})
}
}
// updateLastGITPushTime updates the repo's last git push time.
func (c *Controller) updateLastGITPushTime(
ctx context.Context,
repo *types.Repository,
) {
newRepo, err := c.repoStore.UpdateOptLock(ctx, repo, func(r *types.Repository) error {
r.LastGITPush = time.Now().UnixMilli()
return nil
})
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msgf("failed to update last git push time for repo %q", repo.Path)
return
}
*repo = *newRepo
}
// logForcePush detects and logs force pushes to the default branch.
func (c *Controller) logForcePush(
ctx context.Context,
repo *types.Repository,
principalID int64,
refUpdates []hook.ReferenceUpdate,
forcePushStatus refForcePushMap,
) {
if repo.DefaultBranch == "" {
return
}
defaultBranchRef := gitReferenceNamePrefixBranch + repo.DefaultBranch
_, exists := forcePushStatus[defaultBranchRef]
if !exists {
return
}
var defaultBranchUpdate *hook.ReferenceUpdate
for i := range refUpdates {
if refUpdates[i].Ref == defaultBranchRef && !refUpdates[i].New.IsNil() {
defaultBranchUpdate = &refUpdates[i]
break
}
}
if defaultBranchUpdate == nil {
return
}
principal, err := c.principalStore.Find(ctx, principalID)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("failed to find principal who force pushed to default branch")
return
}
err = c.auditService.Log(ctx,
*principal,
audit.NewResource(
audit.ResourceTypeRepository,
repo.Identifier,
audit.RepoPath,
repo.Path,
audit.BypassedResourceType,
audit.BypassedResourceTypeCommit,
audit.ResourceName,
fmt.Sprintf(
audit.BypassSHALabelFormat,
repo.DefaultBranch,
defaultBranchUpdate.New.String()[0:6],
),
),
audit.ActionForcePush,
paths.Parent(repo.Path),
audit.WithOldObject(audit.CommitObject{
CommitSHA: defaultBranchUpdate.Old.String(),
RepoPath: repo.Path,
}),
audit.WithNewObject(audit.CommitObject{
CommitSHA: defaultBranchUpdate.New.String(),
RepoPath: repo.Path,
}),
)
if err != nil {
log.Ctx(ctx).Warn().Err(err).Msg("failed to insert audit log for force push")
}
}

View File

@ -1,422 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"fmt"
"strings"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/gotidy/ptr"
"github.com/rs/zerolog"
"golang.org/x/exp/slices"
)
// allowedRepoStatesForPush lists repository states that git push is allowed for internal and external calls.
var allowedRepoStatesForPush = []enum.RepoState{enum.RepoStateActive, enum.RepoStateMigrateGitPush}
// PreReceive executes the pre-receive hook for a git repository.
func (c *Controller) PreReceive(
ctx context.Context,
rgit RestrictedGIT,
session *auth.Session,
in types.GithookPreReceiveInput,
) (hook.Output, error) {
output := hook.Output{}
defer func() {
logOutputFor(ctx, "pre-receive", output)
}()
repo, err := c.getRepoCheckAccess(ctx, session, in.RepoID, enum.PermissionRepoPush)
if err != nil {
return hook.Output{}, err
}
if !in.Internal && repo.Type == enum.RepoTypeLinked {
output.Error = ptr.String("Push not allowed to a linked repository")
return output, nil
}
if !in.Internal && !slices.Contains(allowedRepoStatesForPush, repo.State) {
output.Error = ptr.String(fmt.Sprintf("Push not allowed when repository is in '%s' state", repo.State))
return output, nil
}
if err := c.limiter.RepoSize(ctx, in.RepoID); err != nil {
return hook.Output{}, fmt.Errorf(
"resource limit exceeded: %w", limiter.ErrMaxRepoSizeReached,
)
}
forced := make([]bool, len(in.RefUpdates))
for i, refUpdate := range in.RefUpdates {
forced[i], err = isForcePush(
ctx, rgit, repo.GitUID, in.Environment.AlternateObjectDirs, refUpdate,
)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to check branch ancestor: %w", err)
}
}
refUpdates := groupRefsByAction(in.RefUpdates, forced)
if slices.Contains(refUpdates.branches.deleted, repo.DefaultBranch) {
// Default branch mustn't be deleted.
output.Error = ptr.String(usererror.ErrDefaultBranchCantBeDeleted.Error())
return output, nil
}
// For external calls (git pushes) block modification of pullreq references.
if !in.Internal && c.blockPullReqRefUpdate(refUpdates, repo.State) {
output.Error = ptr.String(usererror.ErrPullReqRefsCantBeModified.Error())
return output, nil
}
protectionRules, err := c.protectionManager.ListRepoRules(
ctx, repo.ID, protection.TypeBranch, protection.TypeTag, protection.TypePush,
)
if err != nil {
return hook.Output{}, fmt.Errorf(
"failed to fetch protection rules for the repository: %w", err,
)
}
var principal *types.Principal
repoActive := repo.State == enum.RepoStateActive
if repoActive {
// TODO: use store.PrincipalInfoCache once we abstracted principals.
principal, err = c.principalStore.Find(ctx, in.PrincipalID)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to find inner principal with id %d: %w", in.PrincipalID, err)
}
}
var ruleViolations []types.RuleViolations
var isRepoOwner bool
// For internal calls - through the application interface (API) - no need to verify protection rules.
if !in.Internal && repoActive {
dummySession := &auth.Session{Principal: *principal, Metadata: nil}
isRepoOwner, err = apiauth.IsRepoOwner(ctx, c.authorizer, dummySession, repo)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to determine if user is repo owner: %w", err)
}
ruleViolations, err = c.checkProtectionRules(
ctx, dummySession, repo, refUpdates, protectionRules, isRepoOwner,
)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to check protection rules: %w", err)
}
if output.Error != nil {
return output, nil
}
}
err = c.preReceiveExtender.Extend(ctx, rgit, session, repo, in, &output)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to extend pre-receive hook: %w", err)
}
if output.Error != nil {
return output, nil
}
if repoActive {
// check secret scanning apart from push rules as it is enabled in repository settings.
err = c.scanSecrets(ctx, rgit, repo, false, nil, in, &output)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to scan secrets: %w", err)
}
if output.Error != nil {
return output, nil
}
violations, err := c.processPushProtection(
ctx, rgit, repo, principal, isRepoOwner, refUpdates, protectionRules, in, &output,
)
if err != nil {
return hook.Output{}, err
}
ruleViolations = append(ruleViolations, violations...)
processRuleViolations(&output, ruleViolations)
}
return output, nil
}
// processPushProtection handles push protection verification for active repositories.
func (c *Controller) processPushProtection(
ctx context.Context,
rgit RestrictedGIT,
repo *types.RepositoryCore,
principal *types.Principal,
isRepoOwner bool,
refUpdates changedRefs,
protectionRules []types.RuleInfoInternal,
in types.GithookPreReceiveInput,
output *hook.Output,
) ([]types.RuleViolations, error) {
pushProtection := c.protectionManager.FilterCreatePushProtection(protectionRules)
out, _, err := pushProtection.PushVerify(
ctx,
protection.PushVerifyInput{
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
Actor: principal,
IsRepoOwner: isRepoOwner,
RepoID: repo.ID,
RepoIdentifier: repo.Identifier,
},
)
if err != nil {
return nil, fmt.Errorf("failed to verify git objects: %w", err)
}
if len(out.Protections) == 0 {
// No push protections to verify.
return []types.RuleViolations{}, nil
}
violationsInput := &protection.PushViolationsInput{
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
Actor: principal,
IsRepoOwner: isRepoOwner,
Protections: out.Protections,
}
err = c.scanSecrets(ctx, rgit, repo, out.SecretScanningEnabled, violationsInput, in, output)
if err != nil {
return nil, fmt.Errorf("failed to scan secrets: %w", err)
}
if err = c.processObjects(
ctx, rgit,
repo, principal, refUpdates,
out.FileSizeLimit, out.PrincipalCommitterMatch, violationsInput,
in, output,
); err != nil {
return nil, fmt.Errorf("failed to process pre-receive objects: %w", err)
}
var violations []types.RuleViolations
if violationsInput.HasViolations() {
pushViolations, err := pushProtection.Violations(ctx, violationsInput)
if err != nil {
return nil, fmt.Errorf("failed to backfill violations: %w", err)
}
violations = pushViolations.Violations
}
return violations, nil
}
func (c *Controller) blockPullReqRefUpdate(refUpdates changedRefs, state enum.RepoState) bool {
if state == enum.RepoStateMigrateGitPush {
return false
}
fn := func(ref string) bool {
return strings.HasPrefix(ref, gitReferenceNamePullReq)
}
return slices.ContainsFunc(refUpdates.other.created, fn) ||
slices.ContainsFunc(refUpdates.other.deleted, fn) ||
slices.ContainsFunc(refUpdates.other.updated, fn) ||
slices.ContainsFunc(refUpdates.other.forced, fn)
}
func (c *Controller) checkProtectionRules(
ctx context.Context,
session *auth.Session,
repo *types.RepositoryCore,
refUpdates changedRefs,
protectionRules []types.RuleInfoInternal,
isRepoOwner bool,
) ([]types.RuleViolations, error) {
branchProtection := c.protectionManager.FilterCreateBranchProtection(protectionRules)
tagProtection := c.protectionManager.FilterCreateTagProtection(protectionRules)
var ruleViolations []types.RuleViolations
var errCheckAction error
//nolint:unparam
checkAction := func(
refProtection protection.RefProtection,
refAction protection.RefAction,
refType protection.RefType,
names []string,
) {
if errCheckAction != nil || len(names) == 0 {
return
}
violations, err := refProtection.RefChangeVerify(ctx, protection.RefChangeVerifyInput{
ResolveUserGroupID: c.userGroupService.ListUserIDsByGroupIDs,
Actor: &session.Principal,
AllowBypass: true,
IsRepoOwner: isRepoOwner,
Repo: repo,
RefAction: refAction,
RefType: refType,
RefNames: names,
})
if err != nil {
errCheckAction = fmt.Errorf("failed to verify protection rules for git push: %w", err)
return
}
ruleViolations = append(ruleViolations, violations...)
}
checkAction(
branchProtection, protection.RefActionCreate,
protection.RefTypeBranch, refUpdates.branches.created,
)
checkAction(
branchProtection, protection.RefActionDelete,
protection.RefTypeBranch, refUpdates.branches.deleted,
)
checkAction(
branchProtection, protection.RefActionUpdate,
protection.RefTypeBranch, refUpdates.branches.updated,
)
checkAction(
branchProtection, protection.RefActionUpdateForce,
protection.RefTypeBranch, refUpdates.branches.forced,
)
checkAction(
tagProtection, protection.RefActionCreate,
protection.RefTypeTag, refUpdates.tags.created,
)
checkAction(
tagProtection, protection.RefActionDelete,
protection.RefTypeTag, refUpdates.tags.deleted,
)
checkAction(
tagProtection, protection.RefActionUpdateForce,
protection.RefTypeTag, refUpdates.tags.forced,
)
if errCheckAction != nil {
return nil, errCheckAction
}
return ruleViolations, nil
}
func processRuleViolations(
output *hook.Output,
ruleViolations []types.RuleViolations,
) {
if len(ruleViolations) == 0 {
return
}
var criticalViolation bool
for _, ruleViolation := range ruleViolations {
criticalViolation = criticalViolation || ruleViolation.IsCritical()
for _, violation := range ruleViolation.Violations {
var message string
if ruleViolation.Bypassed {
message = fmt.Sprintf("Bypassed rule %q: %s", ruleViolation.Rule.Identifier, violation.Message)
} else {
message = fmt.Sprintf("Rule %q violation: %s", ruleViolation.Rule.Identifier, violation.Message)
}
output.Messages = append(output.Messages, message)
}
}
if criticalViolation {
output.Error = ptr.String("Blocked by protection rules.")
}
}
type changes struct {
created []string
deleted []string
updated []string
forced []string
}
func (c *changes) groupByAction(
refUpdate hook.ReferenceUpdate,
name string,
forced bool,
) {
switch {
case refUpdate.Old.IsNil():
c.created = append(c.created, name)
case refUpdate.New.IsNil():
c.deleted = append(c.deleted, name)
case forced:
c.forced = append(c.forced, name)
default:
c.updated = append(c.updated, name)
}
}
type changedRefs struct {
branches changes
tags changes
other changes
}
func (c *changedRefs) hasOnlyDeletedBranches() bool {
if len(c.branches.created) > 0 || len(c.branches.updated) > 0 || len(c.branches.forced) > 0 {
return false
}
return true
}
func isBranch(ref string) bool {
return strings.HasPrefix(ref, gitReferenceNamePrefixBranch)
}
func isTag(ref string) bool {
return strings.HasPrefix(ref, gitReferenceNamePrefixTag)
}
func groupRefsByAction(refUpdates []hook.ReferenceUpdate, forced []bool) (c changedRefs) {
for i, refUpdate := range refUpdates {
switch {
case isBranch(refUpdate.Ref):
branchName := refUpdate.Ref[len(gitReferenceNamePrefixBranch):]
c.branches.groupByAction(refUpdate, branchName, forced[i])
case isTag(refUpdate.Ref):
tagName := refUpdate.Ref[len(gitReferenceNamePrefixTag):]
c.tags.groupByAction(refUpdate, tagName, forced[i])
default:
c.other.groupByAction(refUpdate, refUpdate.Ref, false)
}
}
return
}
func loggingWithRefUpdate(refUpdate hook.ReferenceUpdate) func(c zerolog.Context) zerolog.Context {
return func(c zerolog.Context) zerolog.Context {
return c.Str("ref", refUpdate.Ref).Str("old_sha", refUpdate.Old.String()).Str("new_sha", refUpdate.New.String())
}
}

View File

@ -1,174 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"fmt"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/types"
"github.com/gotidy/ptr"
)
func (c *Controller) processObjects(
ctx context.Context,
rgit RestrictedGIT,
repo *types.RepositoryCore,
principal *types.Principal,
refUpdates changedRefs,
sizeLimit int64,
principalCommitterMatch bool,
violationsInput *protection.PushViolationsInput,
in types.GithookPreReceiveInput,
output *hook.Output,
) error {
if refUpdates.hasOnlyDeletedBranches() {
return nil
}
// TODO: Remove this once push rules implementation and migration are complete.
settingsSizeLimit, err := settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeyFileSizeLimit,
settings.DefaultFileSizeLimit,
)
if err != nil {
return fmt.Errorf("failed to check settings for file size limit: %w", err)
}
if sizeLimit == 0 || (settingsSizeLimit > 0 && sizeLimit > settingsSizeLimit) {
sizeLimit = settingsSizeLimit
}
// TODO: Remove this once push rules implementation and migration are complete.
if !principalCommitterMatch {
principalCommitterMatch, err = settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeyPrincipalCommitterMatch,
settings.DefaultPrincipalCommitterMatch,
)
if err != nil {
return fmt.Errorf("failed to check settings for principal committer match: %w", err)
}
}
gitLFSEnabled, err := settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeyGitLFSEnabled,
settings.DefaultGitLFSEnabled,
)
if err != nil {
return fmt.Errorf("failed to check settings for Git LFS enabled: %w", err)
}
if sizeLimit == 0 && !principalCommitterMatch && !gitLFSEnabled {
return nil
}
preReceiveObjsIn := git.ProcessPreReceiveObjectsParams{
ReadParams: git.ReadParams{
RepoUID: repo.GitUID,
AlternateObjectDirs: in.Environment.AlternateObjectDirs,
},
}
if sizeLimit > 0 {
preReceiveObjsIn.FindOversizeFilesParams = &git.FindOversizeFilesParams{
SizeLimit: sizeLimit,
}
}
if principalCommitterMatch && principal != nil && !in.Internal {
preReceiveObjsIn.FindCommitterMismatchParams = &git.FindCommitterMismatchParams{
PrincipalEmail: principal.Email,
}
}
if gitLFSEnabled {
preReceiveObjsIn.FindLFSPointersParams = &git.FindLFSPointersParams{}
}
preReceiveObjsOut, err := rgit.ProcessPreReceiveObjects(
ctx,
preReceiveObjsIn,
)
if err != nil {
return fmt.Errorf("failed to process pre-receive objects: %w", err)
}
if preReceiveObjsOut.FindOversizeFilesOutput != nil &&
len(preReceiveObjsOut.FindOversizeFilesOutput.FileInfos) > 0 {
printOversizeFiles(
output,
preReceiveObjsOut.FindOversizeFilesOutput.FileInfos,
preReceiveObjsOut.FindOversizeFilesOutput.Total,
sizeLimit,
)
}
if preReceiveObjsOut.FindCommitterMismatchOutput != nil &&
len(preReceiveObjsOut.FindCommitterMismatchOutput.CommitInfos) > 0 {
printCommitterMismatch(
output,
preReceiveObjsOut.FindCommitterMismatchOutput.CommitInfos,
preReceiveObjsIn.FindCommitterMismatchParams.PrincipalEmail,
preReceiveObjsOut.FindCommitterMismatchOutput.Total,
)
}
if preReceiveObjsOut.FindLFSPointersOutput != nil &&
len(preReceiveObjsOut.FindLFSPointersOutput.LFSInfos) > 0 {
objIDs := make([]string, len(preReceiveObjsOut.FindLFSPointersOutput.LFSInfos))
for i, info := range preReceiveObjsOut.FindLFSPointersOutput.LFSInfos {
objIDs[i] = info.ObjID
}
existingObjs, err := c.lfsStore.FindMany(ctx, in.RepoID, objIDs)
if err != nil {
return fmt.Errorf("failed to find lfs objects: %w", err)
}
//nolint:lll
if len(existingObjs) != len(objIDs) {
output.Error = ptr.String(
"Changes blocked by unknown Git LFS objects. Please try `git lfs push --all` or check if LFS is setup properly.")
printLFSPointers(
output,
preReceiveObjsOut.FindLFSPointersOutput.LFSInfos,
preReceiveObjsOut.FindLFSPointersOutput.Total,
)
}
}
violationsInput.FileSizeLimit = sizeLimit
violationsInput.FindOversizeFilesOutput = preReceiveObjsOut.FindOversizeFilesOutput
violationsInput.PrincipalCommitterMatch = principalCommitterMatch
if preReceiveObjsOut.FindCommitterMismatchOutput != nil {
violationsInput.CommitterMismatchCount = preReceiveObjsOut.FindCommitterMismatchOutput.Total
}
return nil
}

View File

@ -1,178 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"fmt"
"time"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/logging"
"github.com/harness/gitness/types"
"github.com/gotidy/ptr"
"github.com/rs/zerolog/log"
)
type secretFinding struct {
git.ScanSecretsFinding
Ref string
}
func (c *Controller) scanSecrets(
ctx context.Context,
rgit RestrictedGIT,
repo *types.RepositoryCore,
scanningEnabled bool,
violationsInput *protection.PushViolationsInput,
in types.GithookPreReceiveInput,
output *hook.Output,
) error {
if !scanningEnabled {
var err error
scanningEnabled, err = settings.RepoGet(
ctx,
c.settings,
repo.ID,
settings.KeySecretScanningEnabled,
settings.DefaultSecretScanningEnabled,
)
if err != nil {
return fmt.Errorf("failed to check settings whether secret scanning is enabled: %w", err)
}
}
if !scanningEnabled {
return nil
}
// scan for secrets
startTime := time.Now()
findings, err := scanSecretsInternal(
ctx,
rgit,
repo,
in,
)
if err != nil {
return fmt.Errorf("failed to scan for git leaks: %w", err)
}
// always print result (handles both no results and results found)
printScanSecretsFindings(output, findings, len(in.RefUpdates) > 1, time.Since(startTime))
// this will be removed when secret scanning check will be moved to push protection
if len(findings) > 0 && violationsInput == nil {
errMsg := fmt.Sprintf("Found %d secret(s) in your code. Push rejected.", len(findings))
output.Error = ptr.String(errMsg)
}
if violationsInput != nil {
violationsInput.SecretScanningEnabled = scanningEnabled
violationsInput.FoundSecretCount = len(findings)
}
return nil
}
func scanSecretsInternal(ctx context.Context,
rgit RestrictedGIT,
repo *types.RepositoryCore,
in types.GithookPreReceiveInput,
) ([]secretFinding, error) {
var baseRevFallBack *string
findings := []secretFinding{}
for _, refUpdate := range in.RefUpdates {
ctx := logging.NewContext(ctx, loggingWithRefUpdate(refUpdate))
log := log.Ctx(ctx)
if refUpdate.New.IsNil() {
log.Debug().Msg("skip deleted reference")
continue
}
// in case the branch was just created - fallback to compare against latest default branch.
baseRev := refUpdate.Old.String() + "^{commit}" //nolint:goconst
rev := refUpdate.New.String() + "^{commit}" //nolint:goconst
//nolint:nestif
if refUpdate.Old.IsNil() {
if baseRevFallBack == nil {
fallbackSHA, fallbackAvailable, err := GetBaseSHAForScanningChanges(
ctx,
rgit,
repo,
in.Environment,
in.RefUpdates,
refUpdate,
)
if err != nil {
return nil, fmt.Errorf("failed to get fallback sha: %w", err)
}
if fallbackAvailable {
log.Debug().Msgf("found fallback sha %q", fallbackSHA)
baseRevFallBack = ptr.String(fallbackSHA.String())
} else {
log.Debug().Msg("no fallback sha available, do full scan instead")
baseRevFallBack = ptr.String("")
}
}
log.Debug().Msgf("new reference, use rev %q as base for secret scanning", *baseRevFallBack)
baseRev = *baseRevFallBack
}
log.Debug().Msg("scan for secrets")
scanSecretsOut, err := rgit.ScanSecrets(ctx, &git.ScanSecretsParams{
ReadParams: git.ReadParams{
RepoUID: repo.GitUID,
AlternateObjectDirs: in.Environment.AlternateObjectDirs,
},
BaseRev: baseRev,
Rev: rev,
GitleaksIgnorePath: git.DefaultGitleaksIgnorePath,
})
if err != nil {
return nil, fmt.Errorf("failed to detect secret leaks: %w", err)
}
if len(scanSecretsOut.Findings) == 0 {
log.Debug().Msg("no new secrets found")
continue
}
log.Debug().Msgf("found %d new secrets", len(scanSecretsOut.Findings))
for _, finding := range scanSecretsOut.Findings {
findings = append(findings, secretFinding{
ScanSecretsFinding: finding,
Ref: refUpdate.Ref,
})
}
}
if len(findings) > 0 {
log.Ctx(ctx).Debug().Msgf("found total of %d new secrets", len(findings))
}
return findings, nil
}

View File

@ -1,210 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"fmt"
"time"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/hook"
"github.com/fatih/color"
)
var (
colorScanHeader = color.New(color.FgHiWhite, color.Underline)
colorScanSummary = color.New(color.FgHiRed, color.Bold)
colorScanSummaryNoFindings = color.New(color.FgHiGreen, color.Bold)
)
func printScanSecretsFindings(
output *hook.Output,
findings []secretFinding,
multipleRefs bool,
duration time.Duration,
) {
findingsCnt := len(findings)
// no results? output success and continue
if findingsCnt == 0 {
output.Messages = append(
output.Messages,
colorScanSummaryNoFindings.Sprintf("No secrets found")+
fmt.Sprintf(" in %s", duration.Round(time.Millisecond)),
"", "", // add two empty lines for making it visually more consumable
)
return
}
output.Messages = append(
output.Messages,
colorScanHeader.Sprintf(
"Push contains %s:",
singularOrPlural("secret", findingsCnt > 1),
),
"", // add empty line for making it visually more consumable
)
for _, finding := range findings {
headerTxt := fmt.Sprintf("%s in %s:%d", finding.RuleID, finding.File, finding.StartLine)
if finding.StartLine != finding.EndLine {
headerTxt += fmt.Sprintf("-%d", finding.EndLine)
}
if multipleRefs {
headerTxt += fmt.Sprintf(" [%s]", finding.Ref)
}
output.Messages = append(
output.Messages,
fmt.Sprintf(" %s", headerTxt),
fmt.Sprintf(" Secret: %s", finding.Secret),
fmt.Sprintf(" Commit: %s", finding.Commit),
fmt.Sprintf(" Details: %s", finding.Description),
fmt.Sprintf(" Fingerprint: %s", finding.Fingerprint),
"", // add empty line for making it visually more consumable
)
}
output.Messages = append(
output.Messages,
colorScanSummary.Sprintf(
"%d %s found",
findingsCnt,
singularOrPlural("secret", findingsCnt > 1),
)+fmt.Sprintf(" in %s", FMTDuration(time.Millisecond)),
"", "", // add two empty lines for making it visually more consumable
)
}
func FMTDuration(d time.Duration) string {
const secondsRounding = time.Second / time.Duration(10)
switch {
case d <= time.Millisecond:
// keep anything under a millisecond untouched
case d < time.Second:
d = d.Round(time.Millisecond) // round under a second to millisecondss
case d < time.Minute:
d = d.Round(secondsRounding) // round under a minute to .1 precision
default:
d = d.Round(time.Second) // keep rest at second precision
}
return d.String()
}
func printOversizeFiles(
output *hook.Output,
oversizeFiles []git.FileInfo,
total int64,
sizeLimit int64,
) {
output.Messages = append(
output.Messages,
colorScanHeader.Sprintf(
"Push contains files exceeding the size limit:",
),
"", // add empty line for making it visually more consumable
)
for _, file := range oversizeFiles {
output.Messages = append(
output.Messages,
fmt.Sprintf(" %s", file.SHA),
fmt.Sprintf(" Size: %dB", file.Size),
"", // add empty line for making it visually more consumable
)
}
output.Messages = append(
output.Messages,
colorScanSummary.Sprintf(
"%d %s found exceeding the size limit of %dB",
total, singularOrPlural("file", total > 1), sizeLimit,
),
"", "", // add two empty lines for making it visually more consumable
)
}
func printCommitterMismatch(
output *hook.Output,
commitInfos []git.CommitInfo,
principalEmail string,
total int64,
) {
output.Messages = append(
output.Messages,
colorScanHeader.Sprintf(
"Push contains commits where committer is not the authenticated user (%s):",
principalEmail,
),
"", // add empty line for making it visually more consumable
)
for _, info := range commitInfos {
output.Messages = append(
output.Messages,
fmt.Sprintf(" %s Committer: %s", info.SHA, info.Committer),
"", // add empty line for making it visually more consumable
)
}
output.Messages = append(
output.Messages,
colorScanSummary.Sprintf(
"%d %s found not matching the authenticated user (%s)",
total, singularOrPlural("commit", total > 1), principalEmail,
),
"", "", // add two empty lines for making it visually more consumable
)
}
func printLFSPointers(
output *hook.Output,
lfsInfos []git.LFSInfo,
total int64,
) {
output.Messages = append(
output.Messages,
colorScanHeader.Sprintf(
"Push references unknown LFS objects:",
),
"", // add empty line for making it visually more consumable
)
for _, info := range lfsInfos {
output.Messages = append(
output.Messages,
fmt.Sprintf(" Object ID: %s", info.ObjID),
fmt.Sprintf(" File SHA : %s", info.SHA),
"", // add empty line for making it visually more consumable
)
}
output.Messages = append(
output.Messages,
colorScanSummary.Sprintf(
"%d %s missing",
total, singularOrPlural("LFS object", total > 1),
),
"", "", // add two empty lines for making it visually more consumable
)
}
func singularOrPlural(noun string, plural bool) string {
if plural {
return noun + "s"
}
return noun
}

View File

@ -1,48 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/git/hook"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// Update executes the update hook for a git repository.
func (c *Controller) Update(
ctx context.Context,
rgit RestrictedGIT,
session *auth.Session,
in types.GithookUpdateInput,
) (hook.Output, error) {
repo, err := c.getRepoCheckAccess(ctx, session, in.RepoID, enum.PermissionRepoPush)
if err != nil {
return hook.Output{}, err
}
output := hook.Output{}
err = c.updateExtender.Extend(ctx, rgit, session, repo, in, &output)
if err != nil {
return hook.Output{}, fmt.Errorf("failed to extend update hook: %w", err)
}
// We currently don't have any update action (nothing planned as of now)
return hook.Output{}, nil
}

View File

@ -1,117 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githook
import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/auth/authz"
eventsgit "github.com/harness/gitness/app/events/git"
eventsrepo "github.com/harness/gitness/app/events/repo"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/services/settings"
"github.com/harness/gitness/app/services/usergroup"
"github.com/harness/gitness/app/sse"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/app/url"
"github.com/harness/gitness/audit"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/hook"
"github.com/google/wire"
)
var WireSet = wire.NewSet(
ProvideController,
ProvideFactory,
)
func ProvideFactory() hook.ClientFactory {
return &ControllerClientFactory{
// fields are set in ProvideController to avoid import
githookCtrl: nil,
git: nil,
}
}
func ProvideController(
authorizer authz.Authorizer,
principalStore store.PrincipalStore,
repoStore store.RepoStore,
repoFinder refcache.RepoFinder,
gitReporter *eventsgit.Reporter,
repoReporter *eventsrepo.Reporter,
git git.Interface,
pullreqStore store.PullReqStore,
urlProvider url.Provider,
protectionManager *protection.Manager,
githookFactory hook.ClientFactory,
limiter limiter.ResourceLimiter,
settings *settings.Service,
preReceiveExtender PreReceiveExtender,
updateExtender UpdateExtender,
postReceiveExtender PostReceiveExtender,
sseStreamer sse.Streamer,
lfsStore store.LFSObjectStore,
auditService audit.Service,
userGroupService usergroup.Service,
) *Controller {
ctrl := NewController(
authorizer,
principalStore,
repoStore,
repoFinder,
gitReporter,
repoReporter,
pullreqStore,
urlProvider,
protectionManager,
limiter,
settings,
preReceiveExtender,
updateExtender,
postReceiveExtender,
sseStreamer,
lfsStore,
auditService,
userGroupService,
)
// TODO: improve wiring if possible
if fct, ok := githookFactory.(*ControllerClientFactory); ok {
fct.githookCtrl = ctrl
fct.git = git
}
return ctrl
}
var ExtenderWireSet = wire.NewSet(
ProvidePreReceiveExtender,
ProvideUpdateExtender,
ProvidePostReceiveExtender,
)
func ProvidePreReceiveExtender() (PreReceiveExtender, error) {
return NewPreReceiveExtender(), nil
}
func ProvideUpdateExtender() (UpdateExtender, error) {
return NewUpdateExtender(), nil
}
func ProvidePostReceiveExtender() (PostReceiveExtender, error) {
return NewPostReceiveExtender(), nil
}

View File

@ -1,118 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
"strconv"
"strings"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
type ActionInput struct {
Action enum.GitspaceActionType `json:"action"`
Identifier string `json:"-"`
SpaceRef string `json:"-"` // Ref of the parent space
}
func (c *Controller) Action(
ctx context.Context,
session *auth.Session,
in *ActionInput,
) (*types.GitspaceConfig, error) {
if err := c.sanitizeActionInput(in); err != nil {
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}
space, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckGitspace(ctx, c.authorizer, session, space.Path, in.Identifier, enum.PermissionGitspaceUse)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
gitspaceConfig, err := c.gitspaceSvc.FindWithLatestInstance(ctx, space.ID, in.Identifier)
if err != nil {
return nil, fmt.Errorf("failed to find gitspace config: %w", err)
}
// check if it's an internal repo
if gitspaceConfig.CodeRepo.Type == enum.CodeRepoTypeGitness {
if gitspaceConfig.CodeRepo.Ref == nil {
return nil, fmt.Errorf("couldn't fetch repo for the user, no ref found: %w", err)
}
repo, err := c.repoFinder.FindByRef(ctx, *gitspaceConfig.CodeRepo.Ref)
if err != nil {
return nil, fmt.Errorf("couldn't fetch repo for the user: %w", err)
}
if err = apiauth.CheckRepo(
ctx,
c.authorizer,
session,
repo,
enum.PermissionRepoView); err != nil {
return nil, err
}
}
gitspaceConfig.BranchURL = c.gitspaceSvc.GetBranchURL(ctx, gitspaceConfig)
// All the actions should be idempotent.
switch in.Action {
case enum.GitspaceActionTypeStart:
err = c.gitspaceLimiter.Usage(ctx, space.ID, gitspaceConfig.InfraProviderResource.InfraProviderType)
if err != nil {
return nil, err
}
c.gitspaceSvc.EmitGitspaceConfigEvent(ctx, *gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStart)
if err = c.gitspaceSvc.StartGitspaceAction(ctx, *gitspaceConfig); err == nil {
gitspaceConfig.State = enum.GitspaceStateStarting
}
return gitspaceConfig, err
case enum.GitspaceActionTypeStop:
c.gitspaceSvc.EmitGitspaceConfigEvent(ctx, *gitspaceConfig, enum.GitspaceEventTypeGitspaceActionStop)
if err = c.gitspaceSvc.StopGitspaceAction(ctx, *gitspaceConfig, time.Now()); err == nil {
gitspaceConfig.State = enum.GitspaceStateStopping
}
return gitspaceConfig, err
case enum.GitspaceActionTypeReset:
c.gitspaceSvc.EmitGitspaceConfigEvent(ctx, *gitspaceConfig, enum.GitspaceEventTypeGitspaceActionReset)
if err = c.gitspaceSvc.ResetGitspaceAction(ctx, *gitspaceConfig); err == nil {
gitspaceConfig.State = enum.GitSpaceStateCleaning
}
return gitspaceConfig, err
default:
return nil, fmt.Errorf("unknown action %s on gitspace : %s", string(in.Action), gitspaceConfig.Identifier)
}
}
func (c *Controller) sanitizeActionInput(in *ActionInput) error {
if err := check.Identifier(in.Identifier); err != nil {
return err
}
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
return ErrGitspaceRequiresParent
}
return nil
}

View File

@ -1,225 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"context"
"fmt"
"strconv"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/types"
"github.com/rs/zerolog/log"
)
// FilterResourcesByCompatibility filters resources based on compatibility with a reference resource.
// It removes any resources that are not compatible according to the IsResourceSpecChangeAllowed criteria.
func FilterResourcesByCompatibility(
ctx context.Context,
filteredResources []*types.InfraProviderResource,
referenceResource *types.InfraProviderResource,
) ([]*types.InfraProviderResource, error) {
if referenceResource == nil {
return nil, fmt.Errorf("referenceResource cannot be nil")
}
compatibleResources := make([]*types.InfraProviderResource, 0)
// Now filter based on compatibility
for _, resource := range filteredResources {
// Skip the current resource itself
if resource.UID == referenceResource.UID {
continue
}
_, err := IsResourceSpecChangeAllowed(referenceResource, resource)
if err != nil {
log.Ctx(ctx).Debug().
Err(err).
Str("resource_id", resource.UID).
Str("reference_id", referenceResource.UID).
Msg("resource compatibility check failed")
} else {
compatibleResources = append(compatibleResources, resource)
}
}
return compatibleResources, nil
}
// IsResourceSpecChangeAllowed checks if the new resource specs are valid and determines if a hard reset is needed.
// Returns (markForHardReset, error) where error contains details about why the validation failed.
func IsResourceSpecChangeAllowed(
existingResource *types.InfraProviderResource,
newResource *types.InfraProviderResource,
) (bool, error) {
// If either resource is nil, we can't compare properly
if existingResource == nil || newResource == nil {
return false, fmt.Errorf("cannot validate resource change: missing resource information")
}
// Validate region is the same
if existingResource.Region != newResource.Region {
return false, usererror.BadRequestf(
"region mismatch: current region '%s' does not match target region '%s'",
existingResource.Region, newResource.Region)
}
// Check zone from metadata if available
existingZone, existingHasZone := existingResource.Metadata["zone"]
newZone, newHasZone := newResource.Metadata["zone"]
// If both resources have zone info, they must match
if existingHasZone && newHasZone && existingZone != newZone {
return false, usererror.BadRequestf(
"zone mismatch: current zone '%s' does not match target zone '%s'",
existingZone, newZone,
)
}
markForInfraReset := false
// Check boot disk changes
needsHardReset, err := validateBootDiskChanges(existingResource.Metadata, newResource.Metadata)
if err != nil {
return false, err
}
if needsHardReset {
markForInfraReset = true
}
// Check persistent disk changes
needsHardReset, err = validatePersistentDiskChanges(existingResource.Metadata, newResource.Metadata)
if err != nil {
return false, err
}
if needsHardReset {
markForInfraReset = true
}
// Check machine type changes
machineTypeResetNeeded := validateMachineTypeChanges(existingResource.Metadata, newResource.Metadata)
markForInfraReset = markForInfraReset || machineTypeResetNeeded
// All checks passed
return markForInfraReset, nil
}
// validatePersistentDiskChanges checks if persistent disk changes are valid and if they require a hard reset.
// Returns (needsHardReset, error).
func validatePersistentDiskChanges(existingMeta, newMeta map[string]string) (bool, error) {
existingDisk, existingOK := existingMeta["persistent_disk_size"]
newDisk, newOK := newMeta["persistent_disk_size"]
if !existingOK || !newOK {
return false, fmt.Errorf(
"invalid persistent disk size format: cannot parse persistent disk sizes for comparison")
}
markForHardReset, err := checkPersistentDiskSizeChange(existingDisk, newDisk)
if err != nil {
return false, err
}
existingDiskType, existingOK := existingMeta["persistent_disk_type"]
newDiskType, newOK := newMeta["persistent_disk_type"]
if !existingOK || !newOK {
return false, fmt.Errorf(
"invalid persistent disk type format: cannot parse persistent disk types for comparison")
}
if existingDiskType != newDiskType {
return false, usererror.BadRequestf(
"persistent disk type change not allowed: from '%s' to '%s'",
existingDiskType, newDiskType)
}
return markForHardReset, nil
}
// validateMachineTypeChanges checks if machine type changes require a hard reset.
// Returns needsHardReset.
func validateMachineTypeChanges(existingMeta, newMeta map[string]string) bool {
existingMachine, existingOK := existingMeta["machine_type"]
newMachine, newOK := newMeta["machine_type"]
if existingOK && newOK && existingMachine != newMachine {
return true
}
return false
}
// validateBootDiskChanges checks if boot disk changes are valid and if they require a hard reset.
// Returns (needsHardReset, error).
func validateBootDiskChanges(existingMeta, newMeta map[string]string) (bool, error) {
markForHardReset := false
// Check boot disk size changes
existingBoot, existingOK := existingMeta["boot_disk_size"]
newBoot, newOK := newMeta["boot_disk_size"]
if !existingOK || !newOK {
return false, fmt.Errorf(
"invalid boot disk size format: cannot parse boot disk sizes for comparison")
}
existingVal, eErr := strconv.Atoi(existingBoot)
newVal, nErr := strconv.Atoi(newBoot)
if eErr != nil || nErr != nil {
return false, fmt.Errorf(
"invalid boot disk size format: cannot parse boot disk sizes for comparison")
}
if newVal != existingVal {
markForHardReset = true
}
// Check boot disk type changes
existingBootType, existingOK := existingMeta["boot_disk_type"]
newBootType, newOK := newMeta["boot_disk_type"]
if !existingOK || !newOK {
return false, fmt.Errorf(
"invalid boot disk type format: cannot parse boot disk types for comparison")
}
if existingBootType != newBootType {
markForHardReset = true
}
return markForHardReset, nil
}
// checkPersistentDiskSizeChange compares existing and new persistent disk sizes.
// and determines if the change is allowed and if hard reset is needed.
// Returns (needsHardReset, error).
//
//nolint:unparam // the bool return value is kept for future extension
func checkPersistentDiskSizeChange(existingDisk, newDisk string) (bool, error) {
existingVal, eErr := strconv.Atoi(existingDisk)
if eErr != nil {
return false, fmt.Errorf("invalid disk size format: cannot parse existing disk size: %w", eErr)
}
newVal, nErr := strconv.Atoi(newDisk)
if nErr != nil {
return false, fmt.Errorf("invalid disk size format: cannot parse new disk size: %w", nErr)
}
// Disallow any changes to persistent disk size
if newVal != existingVal {
return false, fmt.Errorf(
"changing persistent disk size is not allowed: from %d to %d",
existingVal, newVal)
}
// Equal sizes, no hard reset needed
return false, nil
}

View File

@ -1,73 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/gitspace/logutil"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/app/services/gitspace"
"github.com/harness/gitness/app/services/gitspacesettings"
"github.com/harness/gitness/app/services/infraprovider"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database/dbtx"
)
type Controller struct {
authorizer authz.Authorizer
infraProviderSvc *infraprovider.Service
spaceStore store.SpaceStore
spaceFinder refcache.SpaceFinder
gitspaceEventStore store.GitspaceEventStore
tx dbtx.Transactor
statefulLogger *logutil.StatefulLogger
scm *scm.SCM
gitspaceSvc *gitspace.Service
gitspaceLimiter limiter.Gitspace
repoFinder refcache.RepoFinder
settingsService gitspacesettings.Service
}
func NewController(
tx dbtx.Transactor,
authorizer authz.Authorizer,
infraProviderSvc *infraprovider.Service,
spaceStore store.SpaceStore,
spaceFinder refcache.SpaceFinder,
gitspaceEventStore store.GitspaceEventStore,
statefulLogger *logutil.StatefulLogger,
scm *scm.SCM,
gitspaceSvc *gitspace.Service,
gitspaceLimiter limiter.Gitspace,
repoFinder refcache.RepoFinder,
settingsService gitspacesettings.Service,
) *Controller {
return &Controller{
tx: tx,
authorizer: authorizer,
infraProviderSvc: infraProviderSvc,
spaceStore: spaceStore,
spaceFinder: spaceFinder,
gitspaceEventStore: gitspaceEventStore,
statefulLogger: statefulLogger,
scm: scm,
gitspaceSvc: gitspaceSvc,
gitspaceLimiter: gitspaceLimiter,
repoFinder: repoFinder,
settingsService: settingsService,
}
}

View File

@ -1,324 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/paths"
"github.com/harness/gitness/app/services/gitspace"
"github.com/harness/gitness/errors"
"github.com/harness/gitness/store"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
gonanoid "github.com/matoous/go-nanoid"
)
const (
defaultResourceIdentifier = "default"
maxGitspaceConfigIdentifierPrefixLength = 50
suffixLen = 6
)
var (
// ErrGitspaceRequiresParent if the user tries to create a secret without a parent space.
ErrGitspaceRequiresParent = usererror.BadRequest(
"Parent space required - standalone gitspace are not supported.")
)
// CreateInput is the input used for create operations.
type CreateInput struct {
Identifier string `json:"identifier"`
Name string `json:"name"`
SpaceRef string `json:"space_ref"` // Ref of the parent space
IDE enum.IDEType `json:"ide"`
InfraProviderConfigIdentifier string `json:"infra_provider_config_identifier"`
ResourceIdentifier string `json:"resource_identifier"`
ResourceSpaceRef string `json:"resource_space_ref"`
CodeRepoURL string `json:"code_repo_url"`
CodeRepoType enum.GitspaceCodeRepoType `json:"code_repo_type"`
CodeRepoRef *string `json:"code_repo_ref"`
Branch string `json:"branch"`
DevcontainerPath *string `json:"devcontainer_path"`
Metadata map[string]string `json:"metadata"`
SSHTokenIdentifier string `json:"ssh_token_identifier"`
AIAgents []enum.AIAgent `json:"ai_agents"`
}
// Create creates a new gitspace.
func (c *Controller) Create(
ctx context.Context,
session *auth.Session,
in *CreateInput,
) (*types.GitspaceConfig, error) {
space, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref: %w", err)
}
if err = c.sanitizeCreateInput(in); err != nil {
return nil, fmt.Errorf("invalid input: %w", err)
}
if err = apiauth.CheckGitspace(
ctx,
c.authorizer,
session,
space.Path,
"",
enum.PermissionGitspaceCreate); err != nil {
return nil, err
}
// check if it's an internal repo
if in.CodeRepoType == enum.CodeRepoTypeGitness && *in.CodeRepoRef != "" {
repo, err := c.repoFinder.FindByRef(ctx, *in.CodeRepoRef)
if err != nil {
return nil, fmt.Errorf("couldn't fetch repo for the user: %w", err)
}
if err = apiauth.CheckRepo(
ctx,
c.authorizer,
session,
repo,
enum.PermissionRepoView); err != nil {
return nil, err
}
}
identifier, err := buildIdentifier(in.Identifier)
if err != nil {
return nil, fmt.Errorf("could not generate identifier for gitspace config : %q %w", in.Identifier, err)
}
now := time.Now().UnixMilli()
var gitspaceConfig *types.GitspaceConfig
// assume resource to be in same space if it's not explicitly specified.
if in.ResourceSpaceRef == "" {
rootSpaceRef, _, err := paths.DisectRoot(in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("unable to find root space path for %s: %w", in.SpaceRef, err)
}
in.ResourceSpaceRef = rootSpaceRef
}
resourceIdentifier := in.ResourceIdentifier
resourceSpace, err := c.spaceFinder.FindByRef(ctx, in.ResourceSpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref: %w", err)
}
if err = apiauth.CheckInfraProvider(
ctx,
c.authorizer,
session,
resourceSpace.Path,
"",
enum.PermissionInfraProviderView); err != nil {
return nil, err
}
// TODO: Temp fix to ensure the gitspace creation doesnt fail. Once the FE starts sending this field in the
// request, remove this.
if in.InfraProviderConfigIdentifier == "" {
in.InfraProviderConfigIdentifier = defaultResourceIdentifier
}
infraProviderResource, err := c.createOrFindInfraProviderResource(ctx, resourceSpace, resourceIdentifier,
in.InfraProviderConfigIdentifier, now)
if err != nil {
return nil, err
}
err = c.gitspaceLimiter.Usage(ctx, space.ID, infraProviderResource.InfraProviderType)
if err != nil {
return nil, err
}
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
codeRepo := types.CodeRepo{
URL: in.CodeRepoURL,
Ref: in.CodeRepoRef,
Type: in.CodeRepoType,
Branch: in.Branch,
DevcontainerPath: in.DevcontainerPath,
}
principal := session.Principal
principalID := principal.ID
user := types.GitspaceUser{
Identifier: principal.UID,
Email: principal.Email,
DisplayName: principal.DisplayName,
ID: &principalID}
gitspaceConfig = &types.GitspaceConfig{
Identifier: identifier,
Name: in.Name,
IDE: in.IDE,
State: enum.GitspaceStateUninitialized,
SpaceID: space.ID,
SpacePath: space.Path,
Created: now,
Updated: now,
SSHTokenIdentifier: in.SSHTokenIdentifier,
AIAgents: in.AIAgents,
CodeRepo: codeRepo,
GitspaceUser: user,
}
gitspaceConfig.InfraProviderResource = *infraProviderResource
if err = c.settingsService.ValidateGitspaceConfigCreate(
ctx, *infraProviderResource, *gitspaceConfig); err != nil {
return err
}
err = c.gitspaceSvc.Create(ctx, gitspaceConfig)
if err != nil {
return fmt.Errorf("failed to create gitspace config for : %q %w", identifier, err)
}
return nil
})
if err != nil {
return nil, err
}
gitspaceConfig.BranchURL = c.gitspaceSvc.GetBranchURL(ctx, gitspaceConfig)
return gitspaceConfig, nil
}
func (c *Controller) createOrFindInfraProviderResource(
ctx context.Context,
resourceSpace *types.SpaceCore,
resourceIdentifier string,
infraProviderConfigIdentifier string,
now int64,
) (*types.InfraProviderResource, error) {
var resource *types.InfraProviderResource
var err error
resource, err = c.infraProviderSvc.FindResourceByConfigAndIdentifier(ctx, resourceSpace.ID,
infraProviderConfigIdentifier, resourceIdentifier)
if ((err != nil && errors.Is(err, store.ErrResourceNotFound)) || resource == nil) &&
resourceIdentifier == defaultResourceIdentifier {
resource, err = c.autoCreateDefaultResource(ctx, resourceSpace, now)
if err != nil {
return nil, err
}
} else if err != nil {
return nil, fmt.Errorf("could not find infra provider resource : %q %w", resourceIdentifier, err)
}
return resource, err
}
func (c *Controller) autoCreateDefaultResource(
ctx context.Context,
currentSpace *types.SpaceCore,
now int64,
) (*types.InfraProviderResource, error) {
rootSpace, err := c.spaceStore.GetRootSpace(ctx, currentSpace.ID)
if err != nil {
return nil, fmt.Errorf("could not get root space for space %s while autocreating default docker "+
"resource: %w", currentSpace.Path, err)
}
defaultDockerConfig := &types.InfraProviderConfig{
Identifier: defaultResourceIdentifier,
Name: "default docker infrastructure",
Type: enum.InfraProviderTypeDocker,
SpaceID: rootSpace.ID,
SpacePath: rootSpace.Path,
Created: now,
Updated: now,
}
defaultResource := types.InfraProviderResource{
UID: defaultResourceIdentifier,
Name: "Standard Docker Resource",
InfraProviderConfigIdentifier: defaultDockerConfig.Identifier,
InfraProviderType: enum.InfraProviderTypeDocker,
CPU: wrapString("any"),
Memory: wrapString("any"),
Disk: wrapString("any"),
Network: wrapString("standard"),
SpaceID: rootSpace.ID,
SpacePath: rootSpace.Path,
Created: now,
Updated: now,
}
defaultDockerConfig.Resources = []types.InfraProviderResource{defaultResource}
err = c.infraProviderSvc.CreateConfigAndResources(ctx, defaultDockerConfig)
if err != nil {
return nil, fmt.Errorf("could not auto-create the infra provider: %w", err)
}
resource, err := c.infraProviderSvc.FindResourceByConfigAndIdentifier(ctx, rootSpace.ID,
defaultDockerConfig.Identifier, defaultResourceIdentifier)
if err != nil {
return nil, fmt.Errorf("could not find infra provider resource : %q %w", defaultResourceIdentifier, err)
}
return resource, nil
}
func wrapString(str string) *string {
return &str
}
func (c *Controller) sanitizeCreateInput(in *CreateInput) error {
if err := check.Identifier(in.ResourceIdentifier); err != nil {
return err
}
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
return ErrGitspaceRequiresParent
}
return nil
}
func buildIdentifier(identifier string) (string, error) {
toLower := strings.ToLower(identifier)
err := validateIdentifier(toLower)
if err != nil {
return "", err
}
suffixUID, err := gonanoid.Generate(gitspace.AllowedUIDAlphabet, suffixLen)
if err != nil {
return "", fmt.Errorf("could not generate UID for gitspace config: %q %w", toLower, err)
}
return toLower + "-" + suffixUID, nil
}
func validateIdentifier(identifier string) error {
invalidCharPattern := regexp.MustCompile(`[^a-z0-9-]`)
if invalidCharPattern.MatchString(identifier) {
return usererror.BadRequestf("Identifier %q contains invalid characters: only lowercase letters, "+
"digits, and hyphens are allowed", identifier)
}
if len(identifier) > maxGitspaceConfigIdentifierPrefixLength {
return fmt.Errorf("identifier %q length should be upto 50 characters, is %d characters",
identifier, len(identifier))
}
return nil
}

View File

@ -1,38 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Delete(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) error {
err := apiauth.CheckGitspace(ctx, c.authorizer, session, spaceRef, identifier, enum.PermissionGitspaceDelete)
if err != nil {
return fmt.Errorf("failed to authorize: %w", err)
}
return c.gitspaceSvc.DeleteGitspaceByIdentifier(ctx, spaceRef, identifier)
}

View File

@ -1,81 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
var eventMessageMap map[enum.GitspaceEventType]string
func init() {
eventMessageMap = enum.EventsMessageMapping()
}
func (c *Controller) Events(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
page int,
limit int,
) ([]*types.GitspaceEventResponse, int, error) {
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return nil, 0, fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckGitspace(ctx, c.authorizer, session, space.Path, identifier, enum.PermissionGitspaceView)
if err != nil {
return nil, 0, fmt.Errorf("failed to authorize: %w", err)
}
pagination := types.Pagination{
Page: page,
Size: limit,
}
skipEvents := []enum.GitspaceEventType{
enum.GitspaceEventTypeInfraCleanupStart,
enum.GitspaceEventTypeInfraCleanupCompleted,
enum.GitspaceEventTypeInfraCleanupFailed,
}
filter := &types.GitspaceEventFilter{
Pagination: pagination,
QueryKey: identifier,
SkipEvents: skipEvents,
}
events, count, err := c.gitspaceEventStore.List(ctx, filter)
if err != nil {
return nil, 0, fmt.Errorf("failed to list gitspace events for identifier %s: %w", identifier, err)
}
var result = make([]*types.GitspaceEventResponse, len(events))
for index, event := range events {
gitspaceEventResponse := &types.GitspaceEventResponse{
GitspaceEvent: *event,
Message: eventMessageMap[event.Event],
EventTime: time.Unix(0, event.Timestamp).Format(time.RFC3339Nano)}
result[index] = gitspaceEventResponse
}
return result, count, nil
}

View File

@ -1,43 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) Find(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) (*types.GitspaceConfig, error) {
err := apiauth.CheckGitspace(ctx, c.authorizer, session, spaceRef, identifier, enum.PermissionGitspaceView)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
res, err := c.gitspaceSvc.FindWithLatestInstanceWithSpacePath(ctx, spaceRef, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find gitspace: %w", err)
}
return res, nil
}

View File

@ -1,42 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
)
func (c *Controller) FindAllInSameScope(
ctx context.Context,
// todo: integrate with access control.
_ *auth.Session,
spaceRef string,
identifiers []string,
) ([]types.GitspaceConfig, error) {
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find space: %w", err)
}
gitspaceConfigs, err := c.gitspaceSvc.FindAllByIdentifier(ctx, space.ID, identifiers)
if err != nil {
return nil, fmt.Errorf("failed to find gitspaces: %w", err)
}
return gitspaceConfigs, nil
}

View File

@ -1,146 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"errors"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
)
// ListAllGitspaces all the gitspace with given filter.
// DO NOT USE allSpaceIDs = true for cde-manager. This arg is used only in gitness to list all the gitspaces in gitness
// for all. This is useful to list all the gitspaces in OSS for IDE plugins.
func (c *Controller) ListAllGitspaces( // nolint:gocognit
ctx context.Context,
session *auth.Session,
filter types.GitspaceFilter,
allSpaceIDs bool,
) ([]*types.GitspaceConfig, error) {
if allSpaceIDs {
leafSpaceIDs, err := c.fetchAllLeafSpaceIDs(ctx)
if err != nil {
return nil, err
}
filter.SpaceIDs = leafSpaceIDs
}
var result []*types.GitspaceConfig
err := c.tx.WithTx(ctx, func(ctx context.Context) (err error) {
allGitspaceConfigs, _, _, err := c.gitspaceSvc.ListGitspacesWithInstance(ctx, filter, false)
if err != nil {
return fmt.Errorf("failed to list gitspace configs: %w", err)
}
var spacesMap = make(map[int64]string)
for idx := range allGitspaceConfigs {
if spacesMap[allGitspaceConfigs[idx].SpaceID] == "" {
space, findSpaceErr := c.spaceFinder.FindByRef(ctx, allGitspaceConfigs[idx].SpacePath)
if findSpaceErr != nil {
if !errors.Is(findSpaceErr, store.ErrResourceNotFound) {
return fmt.Errorf(
"error fetching space %d: %w", allGitspaceConfigs[idx].SpaceID, findSpaceErr)
}
continue
}
spacesMap[allGitspaceConfigs[idx].SpaceID] = space.Path
}
}
authorizedSpaceIDs, err := c.getAuthorizedSpaces(ctx, session, spacesMap)
if err != nil {
return err
}
finalGitspaceConfigs := c.filter(allGitspaceConfigs, authorizedSpaceIDs)
result = finalGitspaceConfigs
return nil
}, dbtx.TxDefaultReadOnly)
if err != nil {
return nil, err
}
return result, nil
}
func (c *Controller) fetchAllLeafSpaceIDs(ctx context.Context) ([]int64, error) {
opts := &types.SpaceFilter{}
rootSpaces, err := c.spaceStore.GetAllRootSpaces(ctx, opts)
if err != nil {
return nil, fmt.Errorf("failed to get root spaces: %w", err)
}
var leafSpaceIDs []int64
for _, rootSpace := range rootSpaces {
spaceIDs, err := c.spaceStore.GetDescendantsIDs(ctx, rootSpace.ID)
if err != nil {
if !errors.Is(err, store.ErrResourceNotFound) {
return nil, fmt.Errorf("failed to get descendants ids: %w", err)
}
}
leafSpaceIDs = append(leafSpaceIDs, spaceIDs...)
}
return leafSpaceIDs, nil
}
func (c *Controller) filter(
allGitspaceConfigs []*types.GitspaceConfig,
authorizedSpaceIDs map[int64]bool,
) []*types.GitspaceConfig {
return c.getAuthorizedGitspaceConfigs(allGitspaceConfigs, authorizedSpaceIDs)
}
func (c *Controller) getAuthorizedGitspaceConfigs(
allGitspaceConfigs []*types.GitspaceConfig,
authorizedSpaceIDs map[int64]bool,
) []*types.GitspaceConfig {
var authorizedGitspaceConfigs = make([]*types.GitspaceConfig, 0)
for idx := range allGitspaceConfigs {
if authorizedSpaceIDs[allGitspaceConfigs[idx].SpaceID] {
authorizedGitspaceConfigs = append(authorizedGitspaceConfigs, allGitspaceConfigs[idx])
}
}
return authorizedGitspaceConfigs
}
func (c *Controller) getAuthorizedSpaces(
ctx context.Context,
session *auth.Session,
spacesMap map[int64]string,
) (map[int64]bool, error) {
var authorizedSpaceIDs = make(map[int64]bool, 0)
for spaceID, spacePath := range spacesMap {
err := apiauth.CheckGitspace(
ctx, c.authorizer, session, spacePath, "", enum.PermissionGitspaceView,
)
if err != nil && !apiauth.IsNoAccess(err) {
return nil, fmt.Errorf("failed to check gitspace auth for space ID %d: %w", spaceID, err)
}
authorizedSpaceIDs[spaceID] = true
}
return authorizedSpaceIDs, nil
}

View File

@ -1,86 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"encoding/json"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/sse"
"github.com/harness/gitness/livelog"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) LogsStream(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) (<-chan *sse.Event, <-chan error, error) {
err := apiauth.CheckGitspace(ctx, c.authorizer, session, spaceRef, identifier, enum.PermissionGitspaceView)
if err != nil {
return nil, nil, fmt.Errorf("failed to authorize: %w", err)
}
gitspaceConfig, err := c.gitspaceSvc.FindWithLatestInstanceWithSpacePath(ctx, spaceRef, identifier)
if err != nil {
return nil, nil, fmt.Errorf("failed to find gitspace config: %w", err)
}
linec, errc := c.statefulLogger.TailLogStream(ctx, gitspaceConfig.ID)
if linec == nil {
return nil, nil, fmt.Errorf("log stream not present, failed to tail log stream")
}
evenc := make(chan *sse.Event)
errch := make(chan error)
go func() {
defer close(evenc)
defer close(errch)
for {
select {
case <-ctx.Done():
return
case line, ok := <-linec:
if !ok {
return
}
event := sse.Event{
Type: enum.SSETypeLogLineAppended,
Data: marshalLine(line),
}
evenc <- &event
case err = <-errc:
if err != nil {
errch <- err
return
}
}
}
}()
return evenc, errch, nil
}
func marshalLine(line *livelog.Line) []byte {
data, _ := json.Marshal(line)
return data
}

View File

@ -1,89 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
"net/url"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/types/enum"
)
type LookupRepoInput struct {
SpaceRef string `json:"space_ref"` // Ref of the parent space
URL string `json:"url"`
RepoType enum.GitspaceCodeRepoType `json:"repo_type"`
}
var (
ErrInvalidURL = usererror.BadRequest(
"The URL specified is not valid format.")
ErrRepoMissing = usererror.BadRequest(
"There must be URL or Ref specified fir repo.")
ErrBadURLScheme = usererror.BadRequest("The URL is missing scheme, it must start with http or https")
)
func (c *Controller) LookupRepo(
ctx context.Context,
session *auth.Session,
in *LookupRepoInput,
) (*scm.CodeRepositoryResponse, error) {
if err := c.sanitizeLookupRepoInput(in); err != nil {
return nil, fmt.Errorf("invalid input: %w", err)
}
space, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckInfraProvider(ctx, c.authorizer, session, space.Path,
"", enum.PermissionInfraProviderView)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
repositoryRequest := scm.CodeRepositoryRequest{
URL: in.URL,
UserIdentifier: session.Principal.UID,
SpacePath: space.Path,
RepoType: in.RepoType,
UserID: session.Principal.ID,
}
codeRepositoryResponse, err := c.scm.CheckValidCodeRepo(ctx, repositoryRequest)
if err != nil {
return nil, err
}
return codeRepositoryResponse, nil
}
func (c *Controller) sanitizeLookupRepoInput(in *LookupRepoInput) error {
if in.RepoType == "" && in.URL == "" {
return ErrRepoMissing
}
parsedURL, err := url.Parse(in.URL)
if err != nil {
return ErrInvalidURL
}
if parsedURL.Scheme == "" {
return ErrBadURLScheme
}
if _, err := url.ParseRequestURI(parsedURL.RequestURI()); err != nil {
return err
}
return nil
}

View File

@ -1,225 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"context"
"fmt"
"strconv"
"strings"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/api/controller/gitspace/common"
"github.com/harness/gitness/app/api/usererror"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/paths"
gitnessTypes "github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
// UpdateInput is used for updating a gitspace.
type UpdateInput struct {
IDE enum.IDEType `json:"ide"`
ResourceIdentifier string `json:"resource_identifier"`
ResourceSpaceRef string `json:"resource_space_ref"`
Name string `json:"name"`
SSHTokenIdentifier string `json:"ssh_token_identifier"`
Identifier string `json:"-"`
SpaceRef string `json:"-"`
}
func (c *Controller) Update(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
in *UpdateInput,
) (*gitnessTypes.GitspaceConfig, error) {
in.SpaceRef = spaceRef
in.Identifier = identifier
if err := c.sanitizeUpdateInput(in); err != nil {
return nil, fmt.Errorf("failed to sanitize input: %w", err)
}
err := apiauth.CheckGitspace(ctx, c.authorizer, session, spaceRef, identifier, enum.PermissionGitspaceEdit)
if err != nil {
return nil, fmt.Errorf("failed to authorize: %w", err)
}
gitspaceConfig, err := c.gitspaceSvc.FindWithLatestInstanceWithSpacePath(ctx, spaceRef, identifier)
if err != nil {
return nil, fmt.Errorf("failed to find gitspace config: %w", err)
}
// Check the gitspace state. Update can be done only in stopped, error or uninitialized state
currentState := gitspaceConfig.State
if currentState != enum.GitspaceStateStopped &&
currentState != enum.GitspaceStateUninitialized {
return nil, usererror.BadRequest(
"Gitspace update can only be performed when gitspace is stopped or uninitialized",
)
}
c.updateIDE(in, gitspaceConfig)
if err := c.handleSSHToken(in, gitspaceConfig); err != nil {
return nil, err
}
if err := c.updateResourceIdentifier(ctx, in, gitspaceConfig); err != nil {
return nil, err
}
// TODO Update with proper locks
err = c.gitspaceSvc.UpdateConfig(ctx, gitspaceConfig)
if err != nil {
return nil, fmt.Errorf("failed to update gitspace config: %w", err)
}
return gitspaceConfig, nil
}
func (c *Controller) updateIDE(in *UpdateInput, gitspaceConfig *gitnessTypes.GitspaceConfig) {
if in.IDE != "" && in.IDE != gitspaceConfig.IDE {
gitspaceConfig.IDE = in.IDE
gitspaceConfig.IsMarkedForReset = true
}
// Always clear SSH token if IDE is VS Code Web
if gitspaceConfig.IDE == enum.IDETypeVSCodeWeb {
gitspaceConfig.SSHTokenIdentifier = ""
}
}
func (c *Controller) handleSSHToken(in *UpdateInput, gitspaceConfig *gitnessTypes.GitspaceConfig) error {
if in.SSHTokenIdentifier != "" {
if gitspaceConfig.IDE == enum.IDETypeVSCodeWeb {
return usererror.BadRequest("SSH token should not be sent with VS Code Web IDE")
}
// For other IDEs, update the token
if in.SSHTokenIdentifier != gitspaceConfig.SSHTokenIdentifier {
gitspaceConfig.SSHTokenIdentifier = in.SSHTokenIdentifier
gitspaceConfig.IsMarkedForReset = true
}
}
return nil
}
func (c *Controller) updateResourceIdentifier(
ctx context.Context,
in *UpdateInput,
gitspaceConfig *gitnessTypes.GitspaceConfig,
) error {
// Handle resource identifier update similar to create, but only if provided
if in.ResourceIdentifier == "" || in.ResourceIdentifier == gitspaceConfig.InfraProviderResource.UID {
return nil
}
if gitspaceConfig.InfraProviderResource.UID == "default" {
return usererror.BadRequest("The default resource cannot be updated in harness open source")
}
// Set resource space reference if not provided
if in.ResourceSpaceRef == "" {
rootSpaceRef, _, err := paths.DisectRoot(in.SpaceRef)
if err != nil {
return fmt.Errorf("unable to find root space path for %s: %w", in.SpaceRef, err)
}
in.ResourceSpaceRef = rootSpaceRef
}
// Find spaces and resources
existingResource, newResource, err := c.getResources(ctx, in, gitspaceConfig)
if err != nil {
return err
}
// Validate the resource spec change
markForInfraReset, err := common.IsResourceSpecChangeAllowed(existingResource, newResource)
if err != nil {
return err
}
gitspaceConfig.IsMarkedForInfraReset = gitspaceConfig.IsMarkedForInfraReset || markForInfraReset
gitspaceConfig.InfraProviderResource = *newResource
return nil
}
func (c *Controller) getResources(
ctx context.Context,
in *UpdateInput,
gitspaceConfig *gitnessTypes.GitspaceConfig,
) (*gitnessTypes.InfraProviderResource, *gitnessTypes.InfraProviderResource, error) {
// Get existing resource space and resource
existingSpace, err := c.spaceFinder.FindByRef(
ctx,
gitspaceConfig.InfraProviderResource.SpacePath,
)
if err != nil {
return nil, nil, fmt.Errorf("failed to find resource space: %w", err)
}
existingResource, err := c.infraProviderSvc.FindResourceByConfigAndIdentifier(
ctx,
existingSpace.ID,
gitspaceConfig.InfraProviderResource.InfraProviderConfigIdentifier,
gitspaceConfig.InfraProviderResource.UID,
)
if err != nil {
return nil, nil, fmt.Errorf(
"could not find existing infra provider resource: %w",
err,
)
}
// Get new resource space and resource
newSpace, err := c.spaceFinder.FindByRef(
ctx,
in.ResourceSpaceRef,
)
if err != nil {
return nil, nil, fmt.Errorf("failed to find resource space: %w", err)
}
newResource, err := c.infraProviderSvc.FindResourceByConfigAndIdentifier(
ctx,
newSpace.ID,
gitspaceConfig.InfraProviderResource.InfraProviderConfigIdentifier,
in.ResourceIdentifier,
)
if err != nil {
return nil, nil, fmt.Errorf(
"could not find infra provider resource %q: %w",
in.ResourceIdentifier,
err,
)
}
return existingResource, newResource, nil
}
func (c *Controller) sanitizeUpdateInput(in *UpdateInput) error {
parentRefAsID, err := strconv.ParseInt(in.SpaceRef, 10, 64)
if (err == nil && parentRefAsID <= 0) || (len(strings.TrimSpace(in.SpaceRef)) == 0) {
return ErrGitspaceRequiresParent
}
//nolint:revive
if err := check.Identifier(in.Identifier); err != nil {
return err
}
return nil
}

View File

@ -1,65 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gitspace
import (
"github.com/harness/gitness/app/api/controller/limiter"
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/gitspace/logutil"
"github.com/harness/gitness/app/gitspace/scm"
"github.com/harness/gitness/app/services/gitspace"
"github.com/harness/gitness/app/services/gitspacesettings"
"github.com/harness/gitness/app/services/infraprovider"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/app/store"
"github.com/harness/gitness/store/database/dbtx"
"github.com/google/wire"
)
// WireSet provides a wire set for this package.
var WireSet = wire.NewSet(
ProvideController,
)
func ProvideController(
tx dbtx.Transactor,
authorizer authz.Authorizer,
infraProviderSvc *infraprovider.Service,
spaceStore store.SpaceStore,
spaceFinder refcache.SpaceFinder,
eventStore store.GitspaceEventStore,
statefulLogger *logutil.StatefulLogger,
scm *scm.SCM,
gitspaceSvc *gitspace.Service,
gitspaceLimiter limiter.Gitspace,
repoFinder refcache.RepoFinder,
settingsService gitspacesettings.Service,
) *Controller {
return NewController(
tx,
authorizer,
infraProviderSvc,
spaceStore,
spaceFinder,
eventStore,
statefulLogger,
scm,
gitspaceSvc,
gitspaceLimiter,
repoFinder,
settingsService,
)
}

View File

@ -1,75 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"github.com/harness/gitness/app/auth/authz"
"github.com/harness/gitness/app/services/infraprovider"
"github.com/harness/gitness/app/services/refcache"
"github.com/harness/gitness/types/enum"
)
const NoResourceIdentifier = ""
type ConfigInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
SpaceRef string `json:"space_ref" yaml:"space_ref"`
Name string `json:"name" yaml:"name"`
Type enum.InfraProviderType `json:"type" yaml:"type"`
Metadata map[string]any `json:"metadata" yaml:"metadata"`
}
type ResourceInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
Name string `json:"name" yaml:"name"`
InfraProviderType enum.InfraProviderType `json:"infra_provider_type" yaml:"infra_provider_type"`
CPU *string `json:"cpu" yaml:"cpu"`
Memory *string `json:"memory" yaml:"memory"`
Disk *string `json:"disk" yaml:"disk"`
Network *string `json:"network" yaml:"network"`
Region string `json:"region" yaml:"region"`
Metadata map[string]string `json:"metadata" yaml:"metadata"`
GatewayHost *string `json:"gateway_host" yaml:"gateway_host"`
GatewayPort *string `json:"gateway_port" yaml:"gateway_port"`
}
type AutoCreateInput struct {
Config ConfigInput `json:"config" yaml:"config"`
Resources []ResourceInput `json:"resources" yaml:"resources"`
}
type TemplateInput struct {
Identifier string `json:"identifier" yaml:"identifier"`
Description string `json:"description" yaml:"description"`
Data string `json:"data" yaml:"data"`
}
type Controller struct {
authorizer authz.Authorizer
spaceFinder refcache.SpaceFinder
infraproviderSvc *infraprovider.Service
}
func NewController(
authorizer authz.Authorizer,
spaceFinder refcache.SpaceFinder,
infraproviderSvc *infraprovider.Service,
) *Controller {
return &Controller{
authorizer: authorizer,
spaceFinder: spaceFinder,
infraproviderSvc: infraproviderSvc,
}
}

View File

@ -1,83 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
// CreateConfig creates a new infra provider config.
func (c *Controller) CreateConfig(
ctx context.Context,
session auth.Session,
in ConfigInput,
) (*types.InfraProviderConfig, error) {
if err := c.sanitizeCreateInput(in); err != nil {
return nil, fmt.Errorf("invalid input: %w", err)
}
parentSpace, err := c.spaceFinder.FindByRef(ctx, in.SpaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref %q : %w", in.SpaceRef, err)
}
if err = apiauth.CheckInfraProvider(
ctx,
c.authorizer,
&session,
parentSpace.Path,
NoResourceIdentifier,
enum.PermissionInfraProviderEdit,
); err != nil {
return nil, err
}
now := time.Now().UnixMilli()
infraProviderConfig := c.MapToInfraProviderConfig(in, parentSpace, now)
err = c.infraproviderSvc.CreateConfig(ctx, infraProviderConfig)
if err != nil {
return nil, fmt.Errorf("unable to create the infraprovider: %q %w", infraProviderConfig.Identifier, err)
}
return infraProviderConfig, nil
}
func (c *Controller) MapToInfraProviderConfig(
in ConfigInput,
space *types.SpaceCore,
now int64,
) *types.InfraProviderConfig {
return &types.InfraProviderConfig{
Identifier: in.Identifier,
Name: in.Name,
SpaceID: space.ID,
SpacePath: space.Path,
Type: in.Type,
Created: now,
Updated: now,
Metadata: in.Metadata,
}
}
func (c *Controller) sanitizeCreateInput(in ConfigInput) error {
if err := check.Identifier(in.Identifier); err != nil {
return err
}
return nil
}

View File

@ -1,143 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
"time"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/check"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) CreateTemplate(
ctx context.Context,
session *auth.Session,
in *TemplateInput,
configIdentifier string,
spaceRef string,
) (*types.InfraProviderTemplate, error) {
now := time.Now().UnixMilli()
parentSpace, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref: %w", err)
}
if err = apiauth.CheckInfraProvider(
ctx,
c.authorizer,
session,
parentSpace.Path,
NoResourceIdentifier,
enum.PermissionInfraProviderEdit,
); err != nil {
return nil, err
}
infraProviderConfig, err := c.infraproviderSvc.Find(ctx, parentSpace, configIdentifier)
if err != nil {
return nil, fmt.Errorf("failed to find infraprovider config by ref: %w", err)
}
providerTemplate := &types.InfraProviderTemplate{
Identifier: in.Identifier,
InfraProviderConfigIdentifier: infraProviderConfig.Identifier,
InfraProviderConfigID: infraProviderConfig.ID,
Description: in.Description,
Data: in.Data,
Version: 0,
SpaceID: parentSpace.ID,
SpacePath: parentSpace.Path,
Created: now,
Updated: now,
}
err = c.infraproviderSvc.CreateTemplate(ctx, providerTemplate)
if err != nil {
return nil, err
}
return providerTemplate, nil
}
func (c *Controller) CreateResources(
ctx context.Context,
session auth.Session,
in []ResourceInput,
configIdentifier string,
spaceRef string,
) ([]types.InfraProviderResource, error) {
if err := c.sanitizeResourceInput(in); err != nil {
return nil, fmt.Errorf("invalid input: %w", err)
}
now := time.Now().UnixMilli()
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return nil, fmt.Errorf("failed to find parent by ref: %w", err)
}
if err = apiauth.CheckInfraProvider(
ctx,
c.authorizer,
&session,
space.Path,
NoResourceIdentifier,
enum.PermissionInfraProviderEdit,
); err != nil {
return nil, err
}
resources := c.MapToResourceEntity(in, space, now)
err = c.infraproviderSvc.CreateResources(ctx, space.ID, resources, configIdentifier)
if err != nil {
return nil, err
}
return resources, nil
}
func (c *Controller) MapToResourceEntity(
in []ResourceInput,
space *types.SpaceCore,
now int64,
) []types.InfraProviderResource {
var resources []types.InfraProviderResource
for _, res := range in {
infraProviderResource := types.InfraProviderResource{
UID: res.Identifier,
InfraProviderType: res.InfraProviderType,
Name: res.Name,
SpaceID: space.ID,
CPU: res.CPU,
Memory: res.Memory,
Disk: res.Disk,
Network: res.Network,
Region: res.Region,
Metadata: res.Metadata,
Created: now,
Updated: now,
SpacePath: space.Path,
}
resources = append(resources, infraProviderResource)
}
return resources
}
func (c *Controller) sanitizeResourceInput(in []ResourceInput) error {
for _, resource := range in {
if err := check.Identifier(resource.Identifier); err != nil {
return err
}
}
return nil
}

View File

@ -1,42 +0,0 @@
// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package infraprovider
import (
"context"
"fmt"
apiauth "github.com/harness/gitness/app/api/auth"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/types/enum"
)
func (c *Controller) DeleteConfig(
ctx context.Context,
session *auth.Session,
spaceRef string,
identifier string,
) error {
space, err := c.spaceFinder.FindByRef(ctx, spaceRef)
if err != nil {
return fmt.Errorf("failed to find space: %w", err)
}
err = apiauth.CheckInfraProvider(ctx, c.authorizer, session, space.Path,
identifier, enum.PermissionInfraProviderDelete)
if err != nil {
return fmt.Errorf("failed to authorize: %w", err)
}
return c.infraproviderSvc.DeleteConfig(ctx, space, identifier, true)
}

Some files were not shown because too many files have changed in this diff Show More